An introduction to Kotlin sealed class

That is, we have a class with a specific number of subclasses. What we get in the end is a concept very similar to an enum. The difference is that in the enum we only have one object per type, while in the sealed classes we can have several objects of the same class. As opposed to enums, subclasses of sealed classes can be instantiated multiple times and can actually contain state.

The important thing about sealed classes is that its subclasses must be declared in the same file as the sealed class itself.

The sealed class feature allows us to define class hierarchies that are restricted in their types, i.e. subclasses. Since all subclasses need to be defined inside the file of the sealed class, there’s no chance of unknown subclasses which the compiler doesn’t know about.

The main advantage of sealed classes reveals itself if it’s used in when expressions. Let’s compare a normal class hierarchy to one of a sealed class handled in a when. First, we’ll create a hierarchy of Mammals and then put it in a method with a when:

open class Mammal(val name: String)
class Cat(val catName: String) : Mammal(catName)
class Human(val humanName: String, val job: String) : Mammal(humanName)

fun greetMammal(mammal: Mammal): String {
    when (mammal) {
        is Human -> return "Hello ${mammal.name}; You're working as a ${mammal.job}"
        is Cat -> return "Hello ${mammal.name}"
        else -> return "Hello unknown"
    }
}

The else is mandatory, otherwise, the compiler will complain. This is because it just cannot verify that all possible cases, i.e. subclasses, are covered here. It may be possible that a subclass Dog is available at any time which is unknown at compile time.

But what if we knew there wouldn’t be other Mammals in our application? We’d want to leave out the else block.

The problem of unknown subclasses can be avoided by sealed classes. Let’s modify the base class Mammal, its’ subclasses can remain the same.

sealed class Mammal(val name: String)

Now we can simply omit the else clause since the compiler can verify that all possible cases are covered because only the subclasses in the file of the sealed class exist, without exception. The method now looks as follows:

fun greetMammal(mammal: Mammal): String {
    when (mammal) {
        is Human -> return "Hello ${mammal.name}; You're working as a ${mammal.job}"
        is Cat -> return "Hello ${mammal.name}"
    }
}

If you leave any of the subclasses out, when will complain and it won’t compile. If you implement them all, you don’t need else statement. And in general it won’t be recommended because that way we’re sure that we’re doing the right thing for all of them.