简体   繁体   中英

null value in init block of parent class

I was creating a very simple kotlin program and saw a weird behavior for parent class.

The code is:

fun makeSalt(name:String) = Spice(name, "non-spicy")
fun main(args: Array<String>) {
    var salt : Spice = Spice("salt", "non-spicy")

    println("Salt heat = ${salt.heat}")

    val spicelist = listOf<Spice>(
        Spice("salt", "non-spicy"),
        Spice("turmeric", "mild"),
        Spice("Pepper", "hot"),
        Spice("Chilli", "hot"),
        Spice("Sugar", "non-spicy")
    )

    val mildSpices = spicelist.filter{it.heat <=5}

    val salt2 = makeSalt("rock salt")

    val bhoot : SubSpice = SubSpice("bhoot", "hot")
}


open class Spice(open var name:String, open var spiciness:String = "mild" ){
    var heat : Int = 5
        get() = when (spiciness){
            "mild"->5
            "hot"->10
            "non-spicy"->1
            else -> 0

        }

    init{
        if(spiciness === null) {println("spiciness is null")}
        else println("Spiciness of ${name} = ${spiciness}; heat = ${heat}")
    }
}

class SubSpice(override var name:String, override var spiciness:String = "hot") : Spice(name, spiciness){

}

When I execute this program, the output is:

Spiciness of salt = non-spicy; heat = 1
Salt heat = 1
Spiciness of salt = non-spicy; heat = 1
Spiciness of turmeric = mild; heat = 5
Spiciness of Pepper = hot; heat = 10
Spiciness of Chilli = hot; heat = 10
Spiciness of Sugar = non-spicy; heat = 1
Spiciness of rock salt = non-spicy; heat = 1
spiciness is null

As you can see, when I created an object of the sub-class, the variable spiciness of parent class becomes null. Can someone explain why is that? I expected it to be null since it also has default parameter of "mild"

You are you using open var when you're not overriding any getter/setter methods.

You're introducing weird initialization conflict since Spice.init (parent class constructor) is invoked before SubSpice.init and by overriding the fields they are no longer initialized alongside parent constructor - instead they will be available once Subspice is constructed.

Remove open keyword from variables in parent class and override var in child constructor, that way fields will be properly initialized in Spice class and its init block should run successfully.

To add extra information to Pawel's answer:

If you are overridding properties using open , the init block in the base class will run before the derived properties have been initialized.

See docs: https://kotlinlang.org/docs/reference/classes.html#derived-class-initialization-order

So if we change the code like Pawel suggested, we end up with something like this:

fun main(args: Array<String>) {
    Spice("salt", "non-spicy")
    Spice(name="spice")
    SubSpice(name = "bhoot", spiciness = "hot")
    SubSpice(name="subspice")
}

open class Spice(var name:String, var spiciness : String = "mild" ) {
    var heat : Int = 5
        get() = when (spiciness){
            "mild"->5
            "hot"->10
            "non-spicy"->1
            else -> 0
        }

    init{
        if(spiciness === null) {println("spiciness is null")}
        else println("Spiciness of ${name} = ${spiciness}; heat = ${heat}")
    }
}

class SubSpice(name: String, spiciness : String = "hot") : Spice(name, spiciness) {
}

Which outputs:

Spiciness of salt = non-spicy; heat = 1
Spiciness of spice = mild; heat = 5
Spiciness of bhoot = hot; heat = 10
Spiciness of subspice = hot; heat = 10

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