Force compilation error with sealed classes
Solution 1:
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
}
Solution 2:
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'.
Solution 3:
Our approach avoids to have the function everywhere when auto-completing. With this solution you also have the when return type in compile time so you can continue using functions of the when return type.
Do exhaustive when (sealedClass) {
is SealedClass.First -> doSomething()
is SealedClass.Second -> doSomethingElse()
}
You can define this object like so:
object Do {
inline infix fun<reified T> exhaustive(any: T?) = any
}
Solution 4:
We can create an extension property on type T with a name that helps explain the purpose
val <T> T.exhaustive: T
get() = this
and then use it anywhere like
when (sealedClass) {
is SealedClass.First -> doSomething()
is SealedClass.Second -> doSomethingElse()
}.exhaustive
It is readable, shows exactly what it does an will show an error if all cases are not covered. Read more here