首页 文章

超级类中的Kotlin Init Block在从它继承时使用null属性

提问于
浏览
2
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中的类继承的理解,首先使用传递的参数调用超类的主构造函数和init块以及辅助构造函数 . 之后,子类可以使用自己的版本覆盖这些属性 .

那么为什么上面的代码导致了所描述的异常......我做错了什么...为什么超类中的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 ... ???详细说明幕后发生的事情 . 这时我在实际的代码库中有类似这样的类的几种情况,我希望从另一个类继承,我想维护属性名称,因为它们......

1 回答

  • 3

    基本上,因为你告诉Kotlin你的子类现在将定义 name ,所以当 Super 中的 init 块被执行时它没有被定义 . 在初始化 SubClass 之前,您将推迟定义 .

    此行为记录在Kotlin网站"Derived class initialization order"下:

    在构造派生类的新实例期间,基类初始化作为第一步完成(仅在评估基类构造函数的参数之前),因此在派生类的初始化逻辑运行之前发生 . ...这意味着,在基类构造函数执行时,尚未初始化在派生类中声明或覆盖的属性 . 如果在基类初始化逻辑中使用任何这些属性(直接或间接,通过另一个重写的开放成员实现),则可能导致不正确的行为或运行时失败 . 因此,设计基类时,应避免在构造函数,属性初始值设定项和初始化块中使用开放成员 . [强调我的]

    FWIW,这类似于一些Java代码分析工具在构造函数中引用非final方法时会抱怨的原因 . 在Kotlin中解决这个问题的方法是不要在超类中的 init 块中引用 open 属性 .

相关问题