Init blocks and the super call in Kotlin are responsible for the correct initialization of objects considering the class hierarchy. These are fundamental mechanisms that influence constructions and inheritance.
In Java, constructors can be invoked explicitly using super(...) or implicitly by default. In Kotlin, everything is different: there is a clear distinction between primary and secondary constructors, and the init block is used for initialization.
The main difficulty is to properly understand when properties are initialized, when init blocks are called, and how the order of constructor calls is constructed, especially when inheriting classes and combining primary/secondary constructors.
Code example:
open class Base(val name: String) { init { println("Base init: $name") } } class Derived(name: String, val age: Int): Base(name) { init { println("Derived init: $name, $age") } } fun main() { Derived("Ivan", 25) } // Output: // Base init: Ivan // Derived init: Ivan, 25
Key features:
In what order are init blocks executed in the inheritance chain?
First, the init blocks of the superclass are executed after its properties are initialized, then the init blocks of the subclass after its own properties.
Code example:
open class A { init { println("A") } } class B: A() { init { println("B") } } fun main() { B() } // A -> B
Can you call super() not from a constructor in Kotlin?
No, the superclass call is performed only as part of the constructor declaration providing parameters to the super constructor.
Can the initialization of a secondary constructor not call the primary in Kotlin?
No, all secondary constructors must delegate the call to either another secondary or the primary constructor. The primary, in turn, calls the super constructor.
A developer tries to access a property initialized in a subclass within the super class's init block:
open class A { init { println(f()) } open fun f() = "A" } class B : A() { private val s = "B" override fun f() = s }``` **Pros:** - Use of polymorphism. **Cons:** - The property s has not yet been initialized during the super class's init call — the result is null or an error. ## Positive case Separating initialization so as not to call overridden methods or properties from the constructor: ```kotlin open class A { init { println("init A") } open fun f() = "A" } class B : A() { private val s = "B" override fun f() = s init { println(f()) } }``` **Pros:** - No access to uninitialized data; - Guaranteed order of initialization. **Cons:** - Increases the amount of code for correct initialization.