In Kotlin ist Vererbung ein zentrales Konzept der objektorientierten Programmierung. Sie erlaubt es einer Klasse, Eigenschaften und Methoden einer anderen Klasse zu übernehmen, wodurch man bestehende Klasseninhalte erweitern oder ihr Verhalten ändern kann.
Im Kontext der Vererbung unterscheidet man hauptsächlich zwischen der Superklasse (oder Basisklasse), die grundlegende Funktionalitäten definiert, und der Unterklasse, die diese Funktionalitäten erbt und sie modifizieren oder erweitern kann.
Syntax der Vererbung in Kotlin
In Kotlin sind Klassen standardgemäß ‚final‘ und daher nicht vererbbar, es sei denn, sie werden ausdrücklich als vererbbar deklariert. Für das Freigeben einer Klasse zur Vererbung verwendet man das Schlüsselwort open, welches an den Anfang der Klassendeklaration gestellt wird:
Bei einer vererbenden Klasse wird nach dem Klassennamen ein Doppelpunkt gesetzt, gefolgt vom Aufruf des primären Konstruktors der Superklasse, von der die Vererbung erfolgt:
- open class MySuperClass { … } – Hiermit wird eine Superklasse mit dem Schlüsselwort open deklariert, was bedeutet, dass sie von anderen Klassen erweitert werden kann.
- class MySubClass: MySuperClass() – In dieser Zeile wird eine Unterklasse namens MySubClass definiert, die von MySuperClass erbt, indem sie den primären Konstruktor der Superklasse aufruft.
Jetzt schauen ein konkretes Beispiel dazu an:
In diesem Code repräsentiert die Klasse Device ein allgemeines Gerät mit der Eigenschaft make (Marke/Hersteller), die standardmäßig „Undefined“ ist, und einer Methode printMake(), um die Marke auszugeben.
Die Klasse Smartphone ist eine Unterklasse von Device und erbt damit alle Eigenschaften und Methoden der Superklasse Device. Das bedeutet, dass ein Objekt der Klasse Smartphone auch über die Eigenschaft make und die Methode printMake() verfügt.
Bei der Vererbung ist es wichtig zu beachten, dass die abgeleitete Klasse den primären Konstruktor (oder, falls keiner vorhanden ist, den Standardkonstruktor) der Superklasse aufrufen muss.
In unserem speziellen Fall definiert die Klasse Device keine primären Konstruktoren explizit. Daher muss die Klasse Smartphone, die von Device erbt, den Standardkonstruktor der Klasse Device aufrufen.
Grundsätzlich gibt es zwei Möglichkeiten, den Konstruktor einer Basisklasse in einer abgeleiteten Klasse aufzurufen. Die erste Methode besteht darin, nach dem Doppelpunkt direkt den Aufruf des Konstruktors der Basisklasse anzugeben.
Ein zweiter Weg, um den Konstruktor der Basisklasse aufzurufen, besteht darin, in der abgeleiteten Klasse einen sekundären Konstruktor zu definieren , der dann mittels des Schlüsselworts super den Konstruktor der Basisklasse aufruft.
Der Konstruktor von Smartphone ruft den Konstruktor der Basisklasse Device auf, was durch den Ausdruck : super() dargestellt wird. In diesem Fall hat die Klasse Device einen Standardkonstruktor (da kein expliziter Konstruktor definiert ist), und Smartphone ruft diesen Konstruktor auf.
Das bedeutet, dass ein Objekt der Klasse Smartphone alle Eigenschaften und Methoden von Device erbt. Wenn man ein Smartphone-Objekt erstellt, wird zunächst der Konstruktor der Basisklasse Device aufgerufen, um die entsprechenden Initialisierungen durchzuführen, bevor irgendwelche spezifischen Initialisierungen für Smartphone durchgeführt werden.
Unabhängig davon, welche Methode genutzt wird, können wir anschließend Objekte der Klasse Smartphone erstellen und für sie die von der Klasse Device geerbten Funktionen nutzen.
Vererbung einer Klasse mit einem primären Konstruktor
Wenn die Superklasse explizit einen Konstruktor (primär oder sekundär) definiert, muss die Unterklasse diesen Konstruktor aufrufen. Die Methoden zum Aufrufen des Konstruktors der Superklasse in der abgeleiteten Unterklasse sind dieselben.
Die erste Methode besteht darin, den Konstruktor direkt nach dem Klassennamen mit einem Doppelpunkt zu benennen und aufzurufen.
In diesem Code setzt die Klasse Device über den Konstruktor die Eigenschaft make. Daher ist auch in der Klasse Smartphone ein Konstruktor definiert, der einen String-Wert entgegennimmt und diesen an den Konstruktor von Device übergibt.