kotlin

What are sealed classes in Kotlin?


I'm a beginner in Kotlin and recently read about Sealed Classes. But from the doc the only think I actually get is that they are exist.

The doc stated, that they are "representing restricted class hierarchies". Besides that I found a statement that they are enums with superpower. Both aspects are actually not clear.

So can you help me with the following questions:

UPDATE: I carefully checked this blog post and still can't wrap my head around that concept. As stated in the post

Benefit

The 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.

Why the compiler doesn't know about other subclasses defined in other files? Even IDE knows that. Just press Ctrl+Alt+B in IDEA on, for instance, List<> definition and all implementations will be shown even in other source files. If a subclass can be defined in some third-party framework, which not used in the application, why should we care about that?


Solution

  • Say you have a domain (your pets) where you know there is a definite enumeration (count) of types. For example, you have two and only two pets (which you will model with a class called MyPet). Meowsi is your cat and Fido is your dog.

    Compare the following two implementations of that contrived example:

    sealed class MyPet
    class Meowsi : MyPet()
    class Fido : MyPet()
    

    Because you have used sealed classes, when you need to perform an action depending on the type of pet, then the possibilities of MyPet are exhausted in two and you can ascertain that the MyPet instance will be exactly one of the two options:

    fun feed(myPet: MyPet): String {
        return when(myPet) {
           is Meowsi -> "Giving cat food to Meowsi!"
           is Fido -> "Giving dog biscuit to Fido!" 
        }
    }
    

    If you don't use sealed classes, the possibilities are not exhausted in two and you need to include an else statement:

    open class MyPet
    class Meowsi : MyPet()
    class Fido : MyPet()
    
    fun feed(myPet: MyPet): String {
        return when(myPet) {
           is Mewosi -> "Giving cat food to Meowsi!"
           is Fido -> "Giving dog biscuit to Fido!" 
           else -> "Giving food to someone else!" //else statement required or compiler error here
        }
    }
    

    In other words, without sealed classes there is not exhaustion (complete coverage) of possibility.

    Note that you could achieve exhaustion of possiblity with Java enum however these are not fully-fledged classes. For example, enum cannot be subclasses of another class, only implement an interface (thanks EpicPandaForce).

    What is the use case for complete exhaustion of possibilities? To give an analogy, imagine you are on a tight budget and your feed is very precious and you want to ensure you don't end up feeding extra pets that are not part of your household.

    Without the sealed class, someone else in your home/application could define a new MyPet:

    class TweetiePie : MyPet() //a bird
    

    And this unwanted pet would be fed by your feed method as it is included in the else statement:

           else -> "Giving food to someone else!" //feeds any other subclass of MyPet including TweetiePie!
    

    Likewise, in your program exhaustion of possibility is desirable because it reduces the number of states your application can be in and reduces the possibility of bugs occurring where you have a possible state where behaviour is poorly defined.

    Hence the need for sealed classes.