首页 > 解决方案 > 抛出 NoWhenBranchMatchedException 时如何穷举?

问题描述

我观察到一个带有详尽when语句的 LiveData,它如何NoWhenBranchMatchedException在运行时抛出一个?在编译时没有检查详尽性吗?

enum class MyEnum { Foo, Bar }

liveData.observe(viewLifecycleOwner) { enumInstance ->
  val instance: MyEnum = when (enumInstance) {
    is Foo -> {}
    is Bar -> {}
  }
}

标签: kotlinandroid-livedatakotlin-interopkotlin-null-safetykotlin-java-interop

解决方案


为什么会发生

因为 LiveData 是用 Java 编写的 API,在 Java 中任何类型都可以为 null,所以 Kotlin 的 null-safety 不会在编译时强制执行。!IntelliJ IDE 使用后缀引用这些类型:

T!意思是“TT?”(来源

enumInstance可能是null,但 Kotlin 不会对其进行任何 null 检查,因为它Observer是一个 Java 接口。这就是为什么when编译器仍然认为该语句是详尽无遗的,即使它不是。将示例中的 LiveData 的值设置为 null 将导致NoWhenBranchMatchedException在运行时抛出 a。

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 -> {}
  }
})

Java 中的任何引用都可能为空,这使得 Kotlin 对来自 Java 的对象的严格空安全性要求不切实际。Java 声明的类型在 Kotlin 中以特定方式处理,称为平台类型。此类类型的空检查被放宽,因此对它们的安全保证与 Java [...] 中的相同(来源

你可以做些什么来防止它

具有编译时安全性

如果它来自 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
  }
})

没有编译时安全

您可以简单地避免使用nullwith LiveData(以及其他地方使用 Java 互操作)。除非您发布,否则观察者不会收到 null 。

liveData.value = Foo
liveData.observe(viewLifecycleOwner, Observer { enumInstance ->
  // I'm sure I won't receive null
})

但是,代码不断变化。即使您今天的代码从未发布过null,它也可能有一天会发布,并且使用这种方法,任何可空性错误只会在运行时出现。


推荐阅读