With sealed classes you can use exhaustive when
expressions and omit the else
clause when the expression returns a result:
sealed class SealedClass { class First : SealedClass() class Second : SealedClass() } fun test(sealedClass: SealedClass) : String = when (sealedClass) { is SealedClass.First -> "First" is SealedClass.Second -> "Second" }
Now if I were to add a Third
to SealedClass
, the compiler will complain that the when
expression in test()
is not exhaustive, and I need to add a clause for Third
or else
.
I am wondering however if this check can also be enforced when test()
does not return anything:
fun test(sealedClass: SealedClass) { when (sealedClass) { is SealedClass.First -> doSomething() is SealedClass.Second -> doSomethingElse() } }
This snippet does not break if Third
is added. I can add a return
statement before when
, but this could easily be forgotten and may break if the return type of one of the clauses is not Unit
.
How can I make sure I don't forget to add a branch to my when
clauses?
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.
What are sealed classes? Sealed classes represent a restricted class hierarchy. This allows you to define subclasses within the scope of the parent function, allowing you to represent hierarchies. In this case, the child or subclass can be of any type, a data class, an object, a regular class, or another sealed class.
The main purpose of a sealed class is to take away the inheritance feature from the class users so they cannot derive a class from it. One of the best usage of sealed classes is when you have a class with static members. For example, the Pens and Brushes classes of the System.
Sealed classes are used to restrict the users from inheriting the class. A class can be sealed by using the sealed keyword. The keyword tells the compiler that the class is sealed, and therefore, cannot be extended. No class can be derived from a sealed class.
The way to enforce exhaustive when
is to make it an expression by using its value:
sealed class SealedClass { class First : SealedClass() class Second : SealedClass() class Third : SealedClass() } fun test(sealedClass: SealedClass) { val x = when (sealedClass) { is SealedClass.First -> doSomething() is SealedClass.Second -> doSomethingElse() } // ERROR here // or when (sealedClass) { is SealedClass.First -> doSomething() is SealedClass.Second -> doSomethingElse() }.let {} // ERROR here }
In inspiration by Voddan's answer, you can build a property called safe
you can use:
val Any?.safe get() = Unit
To use:
when (sealedClass) { is SealedClass.First -> doSomething() is SealedClass.Second -> doSomethingElse() }.safe
I think it provides a clearer message than just appending .let{}
or assigning the result to a value.
There is an open issue on the Kotlin tracker which considers to support 'sealed whens'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With