Blog Infos
Author
Published
Topics
, , , ,
Published

Every project has a time when you would like to pass some data. Frequently, this passed data needs to be different. This is problematic because most languages don’t support it in a clean and readable way.

Ideally, the access should be type-safe, or the complexity of the entire feature will skyrocket. This is where sealed class comes to the rescue

1. What is a sealed class?

sealed class is a class that limits its inheritance to a package name. In short, you could visualize it like this:

How sealed class works visualized

From the image above, you can see that InheritExample2 and InheritExample3 can inherit from Example while SomeClass not!

In general, inheritance is not something we want because it increases complexity and limits the extensibility of a system. However, the system can be simplified if limited to a single package and used to hold data.

Moreover, if you pair sealed class with when in Kotlin, you’ll get a Typesafe way to access different fields and functions defined in inheriting classes (more about that below).

2. How to use it?

It’s straightforward to create a sealed class. Take a look at the following example:

sealed class Example {
    data class ExampleWithName(val name: String) : Example()
    data object EmptyExample : Example()
}

fun main() {
    val exampleWithName = Example.ExampleWithName(name = "Name")
    val emptyExample = Example.EmptyExample
}

If you don’t want to use the parent as the accessor, here Example.InheritingClassName , you can write the classes outside of sealed class block like this:

sealed class Example

data class ExampleWithName(val name: String) : Example()
data object EmptyExample : Example()

fun main() {
    val exampleWithName = ExampleWithName(name = "Name")
    val emptyExample = EmptyExample
}

It’ll work as long as Example , ExampleWithName and EmptyExample are in the same package. However, I recommend using the first version if possible, as we know all the types and what we deal with!

The best practise is to keep them in a single file unless you’re using larger and more complex inheriting classes then splitting them into multiple files in the same package is the way to go

Additionally, note that the Example class itself cannot be created. Only inheriting classes can be instantiated:

fun main() {
    // Compilation error:
    // Cannot access '<init>': it is protected in 'Example'
    // Sealed types cannot be instantiated
    val example = Example()
}
3. Type safe access with when

Kotlin compiler is smart enough to figure out the used type of class at compilation time and when keyword makes our work so much easier!

Just take a look at a function that will change what prints depending on the passed Example class:

sealed class Example {
    data class ExampleWithName(val name: String) : Example()
    data object EmptyExample : Example()
}

fun main() {
    val exampleWithName = Example.ExampleWithName(name = "Name")
    val emptyExample = Example.EmptyExample
    printExample(exampleWithName) // Example with name: Name
    printExample(emptyExample) // Empty example
}

fun printExample(example: Example) {
    when (example) {
        Example.EmptyExample ->
            println("Empty example")

        is Example.ExampleWithName ->
            println("Example with name: ${example.name}")
    }
}

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

No results found.

Jobs

Additionally, when keyword throws a compilation error if all possible variants aren’t handled. If you need a default handler, use else :

fun printExample(example: Example) {
    when (example) {
        Example.EmptyExample ->
            println("Empty example")

        else -> println("Default behavior")
    }
}

Since you’re probably using Android Studio or IntelliJ Idea, there’s a helpful shortcut for writing these expressions. Type when(example) and press (alt + enter):

Shortcut

 

It’ll generate the following code:

fun printExample(example: Example) {
    when (example) {
        Example.EmptyExample -> TODO()
        is Example.ExampleWithName -> TODO()
    }
}
4. Real project usage:

Because of the flexibility of sealed class they’ve found many usages in generic data holders. One of those is a response from the API. The data we get might be an Error or Ok result, meaning something went well or wrong.

Later on, we’re able to safely retrieve the data and change behavior depending on what we got:

sealed class ApiResponse<out T> {
    data class Ok<out T>(val data: T) : ApiResponse<T>()

    data class Error(val exception: Throwable) : ApiResponse<Nothing>()
}

// Usage
fun <T> handleResponse(response: ApiResponse<T>) {
    when (response) {
        is ApiResponse.Error -> // Handle error
        is ApiResponse.Ok -> // Request successfull
    }
}

Thanks for reading! If you’ve learned something new, please follow me for more information and clap! It means a lot!

Based on:

https://kotlinlang.org/docs/sealed-classes.html?source=post_page—–b657dc4bd6e9——————————–

This article is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE
Menu