Wie im vorherigen Artikel bereits beschrieben, wird zur Erstellung eines Objekts ein Konstruktor der Klasse aufgerufen. Wenn keine spezifischen Anweisungen bzgl. eines Konstruktors auf der Klassenebene vorhanden sind, generiert der Compiler standardmäßig einen leeren parameterlosen Konstruktor, wie im folgenden Beispiel dargestellt:
Es besteht aber auch die Möglichkeit eigene Konstruktoren durch das Schlüsselwort constructor an einer Klasse zu definieren. Man unterscheidet zwischen primären und sekundären Konstruktoren. Eine Klasse kann dabei einen primären Konstruktor und mehrere sekundäre Konstruktoren haben. Diese Flexibilität ermöglicht eine differenzierte Initialisierung von Objekten.
Primärer Konstruktor in Kotlin
Der primäre Konstruktor ist Bestandteil des Klassenkopfes und wird direkt nach dem Klassennamen definiert.
Ähnlich wie Funktionen können Konstruktoren in Kotlin mit Parametern versehen werden, die es ermöglichen, Daten von außen zur Objektinitialisierung zu übernehmen. Beispielsweise verfügt der Konstruktor im obenerwähnten Beispiel über den Parameter make vom Typ String.
Falls der primäre Konstruktor weder Annotationen noch Zugriffsmodifikatoren aufweist, kann das Schlüsselwort constructor weggelassen werden.
Initialisator
Ein Initialisator in Kotlin ist ein Codeblock innerhalb einer Klasse, der bei der Erstellung einer Klasseninstanz ausgeführt wird. Diese Blöcke sind mit dem Schlüsselwort init gekennzeichnet. Sie ermöglichen es, Daten, die durch Klassenparameter übernommen werden, an Klasseneigenschaften weiterzugeben und, wenn notwendig, weitere zusätzliche Schritte für Validierung oder Einrichtung durchzuführen. Im nachstehenden Code betrachten wir den einfachen Fall – Übergabe der Daten an Klassenparameter:
Der Konstruktor nimmt den Parameter _make vom Typ String entgegen. Innerhalb der Klasse wird eine Eigenschaft make vom Typ String deklariert. Im init-Block, einem Initialisator, wird dieser Eigenschaft der Wert des Parameters _make zugewiesen. Dies bedeutet, dass bei der Erstellung eines Objekts der Klasse Car, der Wert für make direkt durch den übergebenen Konstruktor-Parameter _make festgelegt wird.
Besonders zu erwähnen dabei, dass die Eigenschaft make keinen Anfangswert hat, weil sie in jedem Fall im Initialisierungsblock initialisiert wird. Bei der Erstellung eines Objekts erhält diese Eigenschaft in jedem Fall einen Wert.
Nun nutzen wir unsere Klasse zur Erstellung mehrerer Objekte:
Es ist wichtig zu berücksichtigen, dass in einer Klasse, wenn ein primärer Konstruktor definiert ist, dieser zur Erstellung eines Objekts genutzt wird. Die gleichzeitige Nutzung eines vom Compiler generierten Standardkonstruktors und eines primären Konstruktors ist nicht möglich.
Es sollte auch beachtet werden, dass in diesem Fall ein Initialisierungsblock nicht notwendig ist, da die Parameter des primären Konstruktors direkt den Eigenschaften zugewiesen werden können. Das führt zu einer erheblichen Vereinfachung unseres Codes, wie im folgenden Beispiel ersichtlich:
Primärer Konstruktor und Eigenschaften
Der primäre Konstruktor kann auch zur Definition von Eigenschaften (Attributen) genutzt werden. Dabei können Eigenschaften direkt im Konstruktor mittels val (für unveränderliche Eigenschaften) oder var (für veränderliche Eigenschaften) definiert werden. Dies erleichtert und verkürzt die Code-Struktur, da Eigenschaften nicht separat im Klassenkörper definiert werden müssen, sondern direkt im Konstruktor zugewiesen werden. Dadurch wird der Code übersichtlicher und effizienter.
Sekundärer Konstruktor
Sekundäre Konstruktoren in Kotlin werden genutzt, um zusätzliche und flexiblere Wege für die Erstellung von Objekten einer Klasse zu bieten. Sie ermöglichen es, verschiedene Konstruktionslogiken innerhalb derselben Klasse zu definieren, die sich in der Anzahl und Art der Parameter unterscheiden können. Dies ist besonders nützlich, wenn Objekte unter verschiedenen Bedingungen oder mit unterschiedlichen Initialisierungswerten erstellt werden sollen.
Sekundäre Konstruktoren werden innerhalb der Klasse definiert. Wenn für die Klasse ein primärer Konstruktor festgelegt ist, ruft der sekundäre Konstruktor den primären mit dem Schlüsselwort this auf.
Der primäre Konstruktor nimmt den Parameter _make entgegen, der zur Initialisierung der unveränderlichen Eigenschaft make dient.
Außerdem wird eine veränderliche Eigenschaft engineSize mit einem Standardwert von 0.00f initialisiert.
Der sekundäre Konstruktor nimmt zwei Parameter, _make und _engineSize, entgegen. Er ruft den primären Konstruktor mit this(_make) auf und setzt dann den Wert von engineSize auf den Wert von _engineSize.
Nun nutzen wir die erstellte Klasse Car und initialisieren zwei unterschiedliche Objekte:
In der main-Funktion werden zwei Objekte der Klasse Car erstellt. Für die Erstellung des Objekts car1 wird der primäre Konstruktor verwendet, der einen Parameter annimmt. Für die Erstellung des Objekts car2 wird der sekundäre Konstruktor mit zwei Parametern angewandt.
Bei Bedarf können auch beliebig viele sekundäre Konstruktoren definiert werden:
In der Klasse Car wurde eine neue Eigenschaft fuelType hinzugefügt, welche die Treibstoffart des Fahrzeugs beschreibt. Es wurde auch ein weiterer Konstruktor hinzugefügt, der drei Parameter annimmt. Um den Code für die Zuweisung der Eigenschaften make und engineSize nicht zu duplizieren, übergibt dieser sekundäre Konstruktor die Einstellung dieser Eigenschaften an einen anderen sekundären Konstruktor, der zwei Parameter annimmt, durch den Aufruf von this(_make, _engineSize). Das bedeutet, dieser Aufruf ruft den ersten sekundären Konstruktor mit zwei Parametern auf.
Ergebnis: