父类(super class)中的 Kotlin Init Block 在继承时使用 null 属性触发

标签 kotlin jvm

open class Super {

    open var name : String = "Name1"

    init {
        println("INIT block fired with : $name")
        name = name.toUpperCase()
        println(name)
    }

}

class SubClass(newName : String) : Super() {

    override var name : String = "Mr. $newName"

}

fun main(args: Array<String>) {

    var obj = SubClass("John")
    println(obj.name)
}

上面的 Kotlin 代码导致以下 TypeCastException :
INIT block fired with : null
Exception in thread "main" kotlin.TypeCastException: null cannot be cast to non-null type java.lang.String
    at Super.<init>(index.kt:7)
    at SubClass.<init>(index.kt:13)
    at IndexKt.main(index.kt:21)

根据我的理解,从 Kotlin 中的类继承时,首先使用传递的参数调用父类(super class)的主构造函数和 init 块以及辅助构造函数。之后子类可以用自己的版本覆盖这些属性。

那么为什么上面的代码会导致如上所述的异常......我做错了什么......为什么父类(super class)中的init块被null触发......?起初,我的猜测是 init 块可能会在实际属性初始化之前被触发,因为它作为主构造函数的一部分执行,但在主构造函数中初始化 name 属性如下给出相同的错误,IDE 会警告我如果所以。
open class Super(open var name : String = "Name1") {

    init {
        println("INIT block fired with : $name")
        name = name.toUpperCase()
        println(name)
    }

}

class SubClass(newName : String) : Super() {

    override var name : String = "Mr. $newName"

}

fun main(args: Array<String>) {

    var obj = SubClass("John")
    println(obj.name)
}

安慰 :
INIT block fired with : null
Exception in thread "main" kotlin.TypeCastException: null cannot be cast to non-null type java.lang.String
    at Super.<init>(index.kt:5)
    at Super.<init>(index.kt:1)
    at SubClass.<init>(index.kt:11)
    at IndexKt.main(index.kt:19)

我在这里做错了什么还是这是一个语言错误......?我该怎么做才能避免错误并使 init 块使用实际传递的值而不是 null 触发......?详细说明幕后发生的事情。此时,在我的实际代码库中,我有几种类似这样的类的情况,我想从另一个类继承,我想保持属性名称,因为它们是......

最佳答案

本质上,因为你告诉 Kotlin 你的子类将定义 name现在,当 init 时它没有定义阻止 Super被执行。您将其定义推迟到 SubClass被初始化。
此行为记录在 Kotlin 网站的 "Derived class initialization order" 下:

During construction of a new instance of a derived class, the base class initialization is done as the first step (preceded only by evaluation of the arguments for the base class constructor) and thus happens before the initialization logic of the derived class is run.

...

It means that, by the time of the base class constructor execution, the properties declared or overridden in the derived class are not yet initialized. If any of those properties are used in the base class initialization logic (either directly or indirectly, through another overridden open member implementation), it may lead to incorrect behavior or a runtime failure. Designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks. [emphasis mine]


FWIW,这类似于如果您在构造函数中引用非 final方法,某些 Java 代码分析工具会提示的原因。在 Kotlin 中解决这个问题的方法是不引用 open您的 init 中的属性父类(super class)中的块。

关于父类(super class)中的 Kotlin Init Block 在继承时使用 null 属性触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49735604/

相关文章:

java - 为什么 Java 编译器内联访问非静态最终字段?

jvm - Java、Java 虚拟机、Java 平台、

java - 详细 JVM 垃圾收集日志的时间戳是收集的开始还是结束?

java - 使用高版本javac编译的jar库

java - 如何使用 JUnit 和 Ant 运行测试?在同一个 JVM 中还是不同的 JVM 中?

kotlin - 如何删除所有指向给定值的键

android - 当我们在 kotlin 中开始新的 Activity 时,为什么我们将 .java 放在 intent 中而不是 .kt

android - Kotlin 协程和 SecurityException

java - 如何从 Java 访问 Kotlin 伴随对象

java - Scala 和 Kotlin 的手动 JAR 编译