I observe a LiveData with an exhaustive when
statement, how can it throw a NoWhenBranchMatchedException
at runtime? Wasn't exhaustiveness checked for at compile time?
enum class MyEnum { Foo, Bar }
liveData.observe(viewLifecycleOwner) { enumInstance ->
val instance: MyEnum = when (enumInstance) {
is Foo -> {}
is Bar -> {}
}
}
Why it can happen
Because LiveData is an API written in Java, and any type can be null in Java, Kotlin's null-safety is not enforced at compile time. The IntelliJ IDEs refer to these types with a
!
suffix:enumInstance
may benull
, but Kotlin doesn't do any null checks on it becauseObserver
is a Java interface. That's why thiswhen
statement is still considered to be exhaustive by the compiler, even though it isn't. Setting the value of the LiveData from the example to null will cause aNoWhenBranchMatchedException
to be thrown at runtime.What you can do to prevent it
With compile-time safety
If it comes from Java, either check for null or assume it to be nullable at any time. Make the type explicit to Kotlin.
What we were doing:
What we should do:
Fixing the example code:
Without compile-time safety
You can simply avoid using
null
withLiveData
(and other places with Java interop). Observers won't receive null unless you post it.However, code changes constantly. Even though your code today never posts
null
, it may come to post it one day, and with this approach any nullability errors will only come up at runtime.