In Kotlin kann jede Klasse einen primären Konstruktor (Hauptkonstruktor) und mehrere sekundäre Konstruktoren (nebenläufige Konstruktoren) haben. Constructor Delegation ist ein Mechanismus, bei dem ein sekundärer Konstruktor entweder direkt den primären Konstruktor oder einen anderen sekundären Konstruktor derselben Klasse aufrufen muss (und letztendlich den primären). Wenn eine Klasse von einer anderen erbt, muss jeder sekundäre Konstruktor der abgeleiteten Klasse den Aufruf des Konstruktors der Superklasse explizit delegieren, wenn dies erforderlich ist.
In Java können Konstruktoren einander direkt mit this() oder super() aufrufen und Konstruktoren in verschiedenen Kombinationen überladen. In Kotlin ist dieses Konzept formalisiert: Eine Klasse kann nur einen primären Konstruktor haben, sekundäre Konstruktoren können die Logik der Delegierung verwenden, die explizit angegeben werden muss.
Eine fehlerhafte Implementierung der Delegierung kann zu Kompilierungsfehlern führen: Es ist nicht zulässig, den primären Konstruktor nicht aufzurufen, wenn er deklariert ist, oder den Superkonstruktor in einer abgeleiteten Klasse nicht aufzurufen, wenn die Basisklasse keinen Standardkonstruktor enthält. Es ist wichtig zu verstehen, wann die init-Blöcke aufgerufen werden, wie Parameter übergeben werden und wie die Delegierung die Reihenfolge der Initialisierung beeinflusst.
Ein Beispiel für grundlegende Delegierung:
class Person(val name: String) { constructor(name: String, age: Int) : this(name) { println("Sekundärer Konstruktor: $name, $age") } }
Wenn Vererbung verwendet wird:
open class Parent(val name: String) class Child : Parent { constructor(name: String) : super(name) { println("Kind Sekundär: $name") } }
Kann ein sekundärer Konstruktor zu nichts delegieren?
Nein, der Compiler wird verlangen, dass explizit this(...) oder super(...) aufgerufen wird, andernfalls gibt es einen Fehler.
In welcher Reihenfolge werden Initialisierungen und init-Blöcke ausgeführt, wenn ein sekundärer Konstruktor verwendet wird?
Zuerst wird immer der primäre Konstruktor und der init-Block (die) aufgerufen, dann der Code des sekunden Konstruktorinitialisierung.
class Demo(val value: String) { init { println("init Block") } constructor(value: String, code: Int) : this(value) { println("sekundär: $code") } } Demo("kotlin", 7) // Ausgabe: // init Block // sekundär: 7
Kann ein Nachfolger nur den primären Konstruktor des Elternteils aufrufen, wenn es dessen sekundären Konstruktor gibt?
Ja, aber nur explizit über super(...), der sekundäre ist für Nachfolger nicht direkt sichtbar.
Im Projekt ruft der sekundäre Konstruktor den primären nicht auf, was bedeutet, dass wichtige Initialisierungen (z.B. das Setzen erforderlicher Felder) nicht stattfinden, was zu Fehlern und Abstürzen zur Laufzeit führt.
Vorteile:
Nachteile:
Alle sekundären Konstruktoren delegieren strikt über this(...), die erforderliche Initialisierung ist im primären/init zentralisiert, die Struktur ist transparent und wartungsfreundlich.
Vorteile:
Nachteile: