在 Android 上的 Kotlin 中,如何在一个或每个循环期间在不可变列表中发生空指针异常?

the*_*osh 2 android kotlin

我在以下代码中收到一个 RARE 空指针异常:

class Artist {
  fun draw(canvas: Canvas, state: State){
    state.drawableObjects.forEach { 
      it.draw(canvas) //NULL POINTER!?! Can not call draw(Canvas) on null object
    }
  }
}

class State {
  var drawableObjects = listOf<DrawableObject>()
    set(value) {
      field = value.filter { it.isVisible } // Why not crash here if null?
    }
}

class DrawableObject(val isVisible: Boolean) {
   fun draw(canvas: Canvas) { }
}
Run Code Online (Sandbox Code Playgroud)

这怎么可能?列表 drawableObjects 是不可变的。它也不允许空对象。当列表更改时,会设置一个全新的列表以防止在绘制调用期间进行修改。

我绝对应该提到涉及多个线程。仅 2 个线程。一个调用 Artist.draw() 和第二个调用 State.drawableObjects = listOf()

Com*_*are 5

我不知道你的数据结构,所以让我们举一个更简单的例子。

假设您尝试List<String>在 Kotlin 中填充 a 。String是不可为空的,因此您的 Kotlin 代码将期望 中的所有元素List都不是null

但是,您可能List<String>来自 Java 代码。在 Java 中,nullList. 而且 Java 代码不一定是你的——它可以来自一个库,比如 Gson。

所以,假设你让 Gson 解析这个 JSON:

[
  "the",
  "quick",
  "brown",
  null,
  "jumped",
  "over",
  "the",
  "lazy",
  null
]
Run Code Online (Sandbox Code Playgroud)

Gson 很乐意将其解析为 Kotlin List<String>,并且 Kotlin 的 JVM 互操作将允许它。但是,当您开始迭代 that 时List<String>,您将NullPointerExceptionnull元素而崩溃。

类似地,Gson 将null在解析缺少某些属性的 JSON 对象时保留 Kotlin属性。假设你有:

data class Something(foo: Int, bar: Int)
Run Code Online (Sandbox Code Playgroud)

和:

{ foo: 42 }
Run Code Online (Sandbox Code Playgroud)

Gson 会很高兴地创建一个设置为的Something实例。barnull

因此,根据您的一条评论,我的猜测是 Gson 正在根据其解析规则为您提供nullList<DrawableObject>。您可以通过将其设置为 aList<DrawableObject?>然后对null元素执行某些操作来解决此问题(过滤掉它们,在forEach循环中跳过它们等)。

  • 我认为这意味着,如果任何值为空,则在 setter 中通过“it.isVisible”进行过滤至少应该抛出一个 NPE,因此最终列表保证只包含非空项目 (2认同)