Sealed classes are used for representing restricted class hierarchies, when a value can have one of the types from a limited set, but cannot have any other type. They are, in a sense, an extension of enum classes: the set of values for an enum type is also restricted, but each enum constant exists only as a single instance, whereas a subclass of a sealed class can have multiple instances which can contain state.
If you are like me, you probably did not understand that on a first read, especially without real-world code samples.
Sealed class rules
- Sealed classes are abstract and can have abstract members.
- Sealed classes cannot be instantiated directly.
- Sealed classes cannot have public constructors (The constructors are private by default).
- Sealed classes can have subclasses, but they must either be in the same file or nested inside of the sealed class declaration.
- Sealed classes subclass can have subclasses outside of the sealed class file.
Declaring a sealed class
1 | sealed class Fruit() |
The important part of the code above is the sealed
modifier. As mentioned in the sealed class rules, a sealed class can have subclasses but they must all be in the same file or nested inside of the sealed class declaration. However, subclasses of the sealed class subclasses do not have to be in the same file.
1 | // Nested |
The key benefit of using sealed classes comes into play when you use them in a when
expression. If it’s possible to verify that the statement covers all cases, you don’t need to add an *else*
clause to the statement. However, this works only if you use *when*
as an expression (using the result) and not as a statement.
1 | // Invalid |
The piece of code above fails because we MUST implement ALL types. The error thrown says:
1 | When expression must be exhaustive, add necessary 'is Orange' branch or else branch instead |
This just means that we must include all types of Fruit in the when expression. Or use a catch-all else block.
1 | override fun getItemViewType(position: Int): Int { |
Bonus: how can we apply sealed classes in a real-world scenario in Android?
There are many ways that sealed classes can be used, but one of my favorites is in a RecyclerView adapter.
Let’s say we want to display a list of fruits in a RecyclerView, where each fruit has its own ViewHolder.
Apple has anAppleViewHolder
and Orange has an OrangeViewHolder
. but we also want to make sure that every new Fruit added has its own ViewHolder explicitly specified, or we throw an exception. We can easily do this with sealed classes.
Show me the code:
1 | sealed class Fruit { |
参考:
https://proandroiddev.com/understanding-kotlin-sealed-classes-65c0adad7015