【问题标题】:ExceptionInInitializerError "Parameter specified as non-null is null" with abstract classExceptionInInitializerError“指定为非空的参数为空”与抽象类
【发布时间】:2019-05-22 06:01:25
【问题描述】:

我需要计算硬编码图像的hash

abstract class ImageData {
    protected abstract val master: List<String>
    val data: Iterable<HexString> = master.map { s -> hex(s) }
    val hash: Int by lazy {
        master.fold(0) { hash, s ->
            31 * hash + s.hashCode()
        }
    }
}

示例图片。

object FooImageData : ImageData() {
    override val master = listOf(
        "424d3684030000000000..."
        // ...
    )
}

例外:

java.lang.ExceptionInInitializerError
    at ....updateGraphics(Graphics.kt:162)
...
 Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter $this$collectionSizeOrDefault
    at kotlin.collections.CollectionsKt__IterablesKt.collectionSizeOrDefault(Iterables.kt)
    at ....ImageData.<init>(ImageData.kt:17)
    at ....FooImageData.<init>(FooImageData.kt:3)
    at ....FooImageData.<clinit>(FooImageData.kt:3)
    at ....updateGraphics(Graphics.kt:162)

....updateGraphics(Graphics.kt:162) 是:

private suspend fun updateGraphics(...) {
    val hash = (FooImageData.hash * 31 + BarImageData.hash)

删除lazy 并不能解决问题。

所有研究都表明参数的顺序可能是一个问题,但这里似乎并非如此 - 或者是这样吗?

使用:

abstract class ImageData {
    abstract val master: List<String>
    // Yes I know the `get()` is unnecessary but for some weird reason that causes `hash` to crash.
    val data: Iterable<HexString> get() = master.map { s -> hex(s) }
    val hash: Int by lazy {
        master.fold(0) { hash, s ->
            31 * hash + s.hashCode()
        }
    }
}

似乎解决了这个问题 - 不知道为什么。

Kotlin 版本Latest stable (1.3)

目标JVM版本:1.6

【问题讨论】:

    标签: kotlin lazy-initialization


    【解决方案1】:

    我认为关键区别在于data 属性上的get(),以及master 是抽象的事实。当这个基类被构造时(也就是子类被创建之前,因为子类的构造函数必须先调用超类的构造函数),基类初始化它的所有成员。您的原始代码有这一行:

    val data: Iterable<HexString> = master.map { s -> hex(s) }
    

    这将获取 master 的值,此时它为 null,因为尚未创建具体的子类,因此还不能覆盖该属性。

    在你更新的 sn-p 中,你有这个:

    val data: Iterable<HexString> get() = master.map { s -> hex(s) }
    

    data 属性现在不需要在抽象基类的初始化期间进行初始化(使用 master 的值)。相反,当data 属性在运行时被调用时,get 函数将被执行。到那时,具体的子类已经构建完成,可以为master 提供适当的值。

    documentation 中有更多详细信息,其中写道:

    在设计基类时,因此应避免使用 open 构造函数、属性初始化器和 init 块中的成员。

    master 属性是abstract,这意味着它是open。)

    【讨论】:

    • 有趣...但是在初始化data 时不会在构造时发生这个问题吗?这里异常发生在运行时请求hash 时。
    • 真的吗?有趣的。我刚刚尝试了您的原始代码,并且在使用FooImageData 时立即收到异常,在我的测试代码中的这一行:val x = FooImageData(即在我调用hash 之前)。您能否分享仅在调用 hash 时显示错误的完整代码?
    • 事实上,我认为你的错误的堆栈跟踪表明它也在你的构造过程中发生:at ....FooImageData.&lt;init&gt;(FooImageData.kt:3)(注意这里的init函数,即在FooImageData的初始化期间) .
    • 哦,我明白了 - 它是在最后一分钟创建对象。
    猜你喜欢
    • 2017-12-06
    • 1970-01-01
    • 2020-05-06
    • 2021-12-09
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    相关资源
    最近更新 更多