抛出 NoWhenBranchMatchedException 时如何才能详尽无遗?
How can an exhaustive when throw NoWhenBranchMatchedException?
我观察到带有详尽 when
语句的 LiveData,它怎么会在运行时抛出 NoWhenBranchMatchedException
?编译时没有检查详尽性吗?
enum class MyEnum { Foo, Bar }
liveData.observe(viewLifecycleOwner) { enumInstance ->
val instance: MyEnum = when (enumInstance) {
is Foo -> {}
is Bar -> {}
}
}
为什么会发生
因为 LiveData 是一个写在 Java 中的 API,并且在 Java 中任何类型都可以为 null,所以 Kotlin 的 null-safety 在编译时没有强制执行。 IntelliJ IDE 使用 !
后缀引用这些类型:
T!
means "T
or T?
" (source)
enumInstance
可能是 null
,但 Kotlin 不会对其进行任何空检查,因为 Observer
是一个 Java 接口。这就是为什么 when
语句仍然被编译器认为是详尽的,即使它不是。将示例中的 LiveData 的值设置为 null 将导致在运行时抛出 NoWhenBranchMatchedException
。
liveData.value = Foo // Perfectly safe at runtime
liveData.value = null // NOT safe at runtime
liveData.observe(viewLifecycleOwner, Observer { enumInstance ->
// enumInstance is "MyEnum!" (nullability unknown to Kotlin)
val instance = when (enumInstance) {
is Foo -> {}
is Bar -> {}
}
})
Any reference in Java may be null, which makes Kotlin's requirements of strict null-safety impractical for objects coming from Java. Types of Java declarations are treated in Kotlin in a specific manner and called platform types. Null-checks are relaxed for such types, so that safety guarantees for them are the same as in Java [...] (source)
你可以做些什么来防止它
具有编译时安全性
如果它来自 Java,则检查是否为 null 或假定它随时可以为 null。使类型对 Kotlin 明确。
我们在做什么:
val unknownNullability = javaApi.getString() // "String!"
unknownNullability.split(',') // Runtime error
我们应该做的:
val knownToBeNullable: String? = javaApi.getString()
knownToBeNullable.split(',') // Compile-time error
修复示例代码:
liveData.observe(viewLifecycleOwner, Observer { enumInstance: MyEnum? ->
// Now I know I may receive null
// This will fail at compile-time:
when (enumInstance) {
is Foo -> {}
is Bar -> {}
// null case is required now
}
})
没有编译时安全
您可以简单地避免将 null
与 LiveData
一起使用(以及其他具有 Java 互操作的地方)。除非你 post 否则观察者不会收到 null。
liveData.value = Foo
liveData.observe(viewLifecycleOwner, Observer { enumInstance ->
// I'm sure I won't receive null
})
但是,代码不断变化。即使您今天的代码从未 post null
,它可能有一天会 post 它,并且使用这种方法,任何可空性错误只会在运行时出现。
我观察到带有详尽 when
语句的 LiveData,它怎么会在运行时抛出 NoWhenBranchMatchedException
?编译时没有检查详尽性吗?
enum class MyEnum { Foo, Bar }
liveData.observe(viewLifecycleOwner) { enumInstance ->
val instance: MyEnum = when (enumInstance) {
is Foo -> {}
is Bar -> {}
}
}
为什么会发生
因为 LiveData 是一个写在 Java 中的 API,并且在 Java 中任何类型都可以为 null,所以 Kotlin 的 null-safety 在编译时没有强制执行。 IntelliJ IDE 使用 !
后缀引用这些类型:
T!
means "T
orT?
" (source)
enumInstance
可能是 null
,但 Kotlin 不会对其进行任何空检查,因为 Observer
是一个 Java 接口。这就是为什么 when
语句仍然被编译器认为是详尽的,即使它不是。将示例中的 LiveData 的值设置为 null 将导致在运行时抛出 NoWhenBranchMatchedException
。
liveData.value = Foo // Perfectly safe at runtime
liveData.value = null // NOT safe at runtime
liveData.observe(viewLifecycleOwner, Observer { enumInstance ->
// enumInstance is "MyEnum!" (nullability unknown to Kotlin)
val instance = when (enumInstance) {
is Foo -> {}
is Bar -> {}
}
})
Any reference in Java may be null, which makes Kotlin's requirements of strict null-safety impractical for objects coming from Java. Types of Java declarations are treated in Kotlin in a specific manner and called platform types. Null-checks are relaxed for such types, so that safety guarantees for them are the same as in Java [...] (source)
你可以做些什么来防止它
具有编译时安全性
如果它来自 Java,则检查是否为 null 或假定它随时可以为 null。使类型对 Kotlin 明确。
我们在做什么:
val unknownNullability = javaApi.getString() // "String!"
unknownNullability.split(',') // Runtime error
我们应该做的:
val knownToBeNullable: String? = javaApi.getString()
knownToBeNullable.split(',') // Compile-time error
修复示例代码:
liveData.observe(viewLifecycleOwner, Observer { enumInstance: MyEnum? ->
// Now I know I may receive null
// This will fail at compile-time:
when (enumInstance) {
is Foo -> {}
is Bar -> {}
// null case is required now
}
})
没有编译时安全
您可以简单地避免将 null
与 LiveData
一起使用(以及其他具有 Java 互操作的地方)。除非你 post 否则观察者不会收到 null。
liveData.value = Foo
liveData.observe(viewLifecycleOwner, Observer { enumInstance ->
// I'm sure I won't receive null
})
但是,代码不断变化。即使您今天的代码从未 post null
,它可能有一天会 post 它,并且使用这种方法,任何可空性错误只会在运行时出现。