Init-blokken en het aanroepen van super in Kotlin zijn verantwoordelijk voor een correcte objectinitialisatie rekening houdend met de klassehiërarchie. Dit zijn fundamentele mechanismen die van invloed zijn op constructies en erfelijkheid.
In Java kunnen constructeurs expliciet worden aangeroepen met super(...) of impliciet standaard. In Kotlin is dit anders: er is een duidelijke scheiding tussen primary en secondary constructors, terwijl init-blokken worden gebruikt voor initialisatie.
De belangrijkste uitdaging is om goed te begrijpen wanneer eigenschappen worden geïnitialiseerd, wanneer init-blokken worden aangeroepen en hoe de volgorde van aanroepen van constructeurs is, vooral bij erfelijkheid van klassen en de combinatie van primary/secondary constructors.
Voorbeeldcode:
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) } // Uitvoer: // Base init: Ivan // Derived init: Ivan, 25
Belangrijke kenmerken:
In welke volgorde worden init-blokken uitgevoerd in een erfelijkheidketen?
Eerst worden de init-blokken van de superklasse uitgevoerd na het initialiseren van zijn eigenschappen, daarna de init-blokken van de subklasse na zijn eigen eigenschappen.
Voorbeeldcode:
open class A { init { println("A") } } class B: A() { init { println("B") } } fun main() { B() } // A -> B
Kan je super() aanroepen buiten een constructor in Kotlin?
Nee, het aanroepen van de superklasse kan alleen worden gedaan als onderdeel van de constructorverklaring met parameters voor de superconstructor.
Kan een secondary constructor in Kotlin de primary constructor niet aanroepen?
Nee, alle secondary constructors zijn verplicht om hun aanroep te delegeren aan een andere secondary of aan de primary constructor. De primary constructor roept op zijn beurt de superconstructor aan.
Een ontwikkelaar probeert toegang te krijgen tot een eigenschap die in de subklasse wordt geïnitialiseerd, in het init-blok van de superklasse:
open class A { init { println(f()) } open fun f() = "A" } class B : A() { private val s = "B" override fun f() = s }``` **Voordelen:** - Gebruik van polymorfisme. **Nadelen:** - Eigenschap s is nog niet geïnitialiseerd tijdens de aanroep van init van de superklasse - resultaat is null of een fout. ## Positieve case De initialisatie scheiden zodat er geen overschreven methoden of eigenschappen vanuit de constructor worden aangeroepen: ```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()) } }``` **Voordelen:** - Geen toegang tot niet-geïnitialiseerde gegevens; - Ge garantie op de volgorde van initialisatie. **Nadelen:** - De hoeveelheid code voor correcte initialisatie neemt toe.