Search code examples
javakotlinoopinheritance

Initializing properties of derived classes in Kotlin


I want to know why exactly my code returns "nullnullnull" instead of expected answer and why does it work correctly when method is used.

Consider the following code:

open class Base(
    open val a: String,
    open val b: String,
    open val c: String,
) {
    val concatenation = a + b + c
}

class Derived(
    override val a: String,
    override val b: String,
    override val c: String
) : Base(a = a, b = b, c = c)

fun main() {
    val derived = Derived(a = "foo", b = "bar", c = "baz")
    println(derived.concatenation)
}

This example prints out "nullnullnull", instead of "foobarbaz".

Though if you replace val concatenation = a + b + c with fun concatenation() = a + b + c in the superclass it seems to work just fine with that method call.

Also, IDEA warns of Accessing non-final property <property> in constructor when using val concatenation = a + b + c, but I'm not sure what exactly does this mean.

Is it something with the order of which Base and Derived classes properties initialized? I thought that it might be that I use concatenation from the Base class, but with inheritance I call Base class constructor too and the same properties with same strings must be initialized.


Solution

  • In my opinion, they should have designed Accessing non-final property <property> in constructor as a compile error, not just a warning, since it creates weird behaviors that are not necessarily even defined in the documentation. For instance, you can easily create a NullPointerException without using !! or any Java interoperational code by doing this.

    "Non-final" means the same thing as "open". You are calling open properties or functions from a constructor, which can create big issues and should never be done.

    The code that assigns the initial values of properties is considered part of the constructor, so your code = a + b + c is part of your constructor and is what triggers the warning.

    Here's what is happening to get the nulls. The Derived class constructor passes the three values to the super class constructor. The super class assigns these values to the super class's backing fields of its a, b, and c properties. Then concatenation is calling the a, b, and c properties to get their values, but since we are actually an instance of Derived, these properties have been overridden and are pointing to different backing fields than the ones that were initialized as part of the super class.

    Also, since we are still executing the super class constructor, the backing fields of the derived class have not been initialized yet, so they still hold null values.