What's an example of using an overridden property in the Base Class initialization (either directly or indirectly)?

Viewed 146

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. When designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks.

I was studying Inheritence from Kotlin docs, and I got stuck here. There was another post which asked a question about this, but the answers were just what the docs said in a different way.

  • To be clear, I understood the data flow between constructors and inheritence. What I couldn't understand was how we can use an overridden property in a base class initialization. It says

It could happen directly or indirectly

  What does this actually mean? How can the base class can somehow access to the overridden property in the derived class?

  • Also, it said

You should therefore avoid using open members in the constructors, property initializers and init blocks.

 So how can we properly use open properties?

EDIT FOR THE COMMENT:

fun main ()
{
    val d = Derived("Test2")
}

open class Base()
{
    open val something:String = "Test1"

    init
    {
        println(something)  //prints null
    }
}

class Derived(override val something: String): Base()
1 Answers

What does this actually mean? How can the base class can somehow access to the overridden property in the derived class?

This is one direct way:

abstract class Base {
    abstract val something: String

    init {
        println(something)
    }
}

class Child(override val something: String): Base()

fun main() {
    Child("Test") // prints null! because the property is not initialized yet
}

This prints null, which is pretty bad for a non-nullable String property.

You should therefore avoid using open members in the constructors, property initializers and init blocks.

So how can we properly use open properties?

You can use these properties in regular methods on the base class (or in custom property getters):

abstract class Base {
    
    abstract val something: String

    fun printSomething() {
        println(something)
    }
}

class Child(override val something: String): Base()

fun main() {
    Child("Test").printSomething() // correctly prints "Test"
}

EDIT: Here are some clarifications regarding the follow-up questions in the comments.

I couldn't quite get why the code in the init block went for the parameter in the child class constructor

I think you might be confused by Kotlin's compact syntax for the primary constructors in general, which probably makes the debugger's flow hard to understand. In the Child declaration, we actually declare many things:

  • the argument something passed to the Child's primary constructor
  • the property something on the Child class, which overrides the parent's one
  • the call to the parent constructor (Base())

When Child() is called, it immediately calls the Base() no-arg constructor, which runs the init block.

We didn't even delegate the base constructor with a parameter or anything, but it still went for the parameter who did the overriding

You might be mixing declarations and runtime here. Although we declare things in the Base class and in the Child class, there is only 1 instance at runtime (an instance of Child) in this example code.

So, in fact, there is only 1 property called something here (only one place in memory). If the init block accesses this property, it can only be the property of the child instance. We don't need to pass anything to the Base constructor because the init block is effectively executed with the data/fields of the Child instance.

Maybe you would be less confused if you saw the Java equivalent of this. It's obvious if you think of the abstract something as a declaration of a getter getSomething(). The child class overrides this getSomething() method and declares a private something field, the getter returns the current value of the field something. But that field is only initialized after the constructor of the parent (and the init block) finished executing.

Related