简体   繁体   中英

Kotlin Init Block in Super class firing with null properties when inheriting from it

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)
}

The above Kotlin code results in the following 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)

As my understanding goes while inheriting from a class in Kotlin, first the primary constructors and init blocks and secondary constructors of super classes are called with passed parameters. After which the subclass can override such properties with its own version.

Then why does the above code results in the exception as described ... What am I doing wrong ... Why does the init block in super class is fired with null ...??? At first my speculation was that the init block might get fired before the actual property initialization as it is executed as a part of primary constructor but initializing the name property in the primary constructor as below gives the same error and the IDE would have warned me if so.

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)
}

Console :

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)

Am I doing something wrong here or is this a language bug...??? What can I do to avoid the error and to make the init blocks fire with the actual passed value and not null ... ??? Elaborate what is happening behind the scene. At this time I have several situations with classes like this in my actual codebase where I want to inherit from another classes, I want to maintain property names as they are...

Essentially, because you tell Kotlin that your subclass is going to be defining name now, it is not defined when the init block in Super is executed. You are deferring definition of that until the SubClass is initialized.

This behavior is documented on the Kotlin website under "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, this is similar to the reason that some Java code analysis tools will complain if you refer to non-final methods in a constructor. The way around this in Kotlin is to not refer to open properties in your init blocks in the superclass.

有同样的麻烦,kotlin 的一个令人厌恶的问题,当在超类调用内部方法后忽略或初始化子类构造函数时,这不是一件安全的事情,如果不是我在 kotlin 中发现的最糟糕的事情。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM