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/