Advertisement
  1. Code
  2. Kotlin

Kotlin von Grund auf neu: Abstrakte Klassen, Schnittstellen, Vererbung und Type Alias

by
Read Time:11 minsLanguages:
This post is part of a series called Kotlin From Scratch.
Kotlin From Scratch: Advanced Properties and Classes
Kotlin From Scratch: Exception Handling

German (Deutsch) translation by Katharina Grigorovich-Nevolina (you can also view the original English article)

Kotlin ist eine moderne Programmiersprache, die zu Java-Bytecode kompiliert wird. Es ist kostenlos und Open Source und verspricht, das Codieren für Android noch lustiger zu machen.

Im vorherigen Artikel haben Sie mehr über Kotlin-Eigenschaften wie späte Initialisierung, Erweiterung und Inline-Eigenschaften erfahren. Darüber hinaus haben Sie in Kotlin auch fortgeschrittene Klassen wie Daten-, Aufzählungs-, verschachtelte und versiegelte Klassen kennengelernt.

In diesem Beitrag lernen Sie weiterhin die objektorientierte Programmierung in Kotlin kennen, indem Sie abstrakte Klassen, Schnittstellen und Vererbung kennenlernen. Als Bonus erfahren Sie auch mehr über Typ-Aliase.

1. Abstrakte Klassen

Kotlin unterstützt abstrakte Klassen - genau wie Java sind dies Klassen, aus denen Sie niemals Objekte erstellen möchten. Eine abstrakte Klasse ist unvollständig oder nutzlos ohne einige konkrete (nicht abstrakte) Unterklassen, aus denen Sie Objekte instanziieren können. Eine konkrete Unterklasse einer abstrakten Klasse implementiert alle in der abstrakten Klasse definierten Methoden und Eigenschaften - andernfalls ist diese Unterklasse auch eine abstrakte Klasse!

Wir erstellen eine abstrakte Klasse mit dem abstract-Modifikator (ähnlich wie in Java).

Beachten Sie, dass nicht alle Mitglieder abstrakt sein müssen. Mit anderen Worten, wir können eine Standardimplementierung der Methode in einer abstrakten Klasse durchführen.

Hier haben wir die nicht abstrakte Funktion fullName() in einer abstrakten Klasse Employee erstellt. Konkrete Klassen (Unterklassen der abstrakten Klasse) können die Standardimplementierung einer abstrakten Methode überschreiben - jedoch nur, wenn für die Methode der Modifikator open angegeben ist (Sie werden in Kürze mehr darüber erfahren).

Wir können den Status auch in abstrakten Klassen speichern.

Auch wenn die abstrakte Klasse keine Methoden definiert, müssen wir eine Unterklasse erstellen, bevor wir sie instanziieren können, wie im folgenden Beispiel.

Unsere Programmer-Klasse erweitert die Employee Abstract-Klasse. In Kotlin verwenden wir ein einzelnes Doppelpunktzeichen (:) anstelle des Java-extends -Schlüsselworts, um eine Klasse zu erweitern oder eine Schnittstelle zu implementieren.

Wir können dann ein Objekt vom Typ Programmer erstellen und Methoden darauf aufrufen - entweder in seiner eigenen Klasse oder in der Oberklasse (Basisklasse).

Eine Sache, die Sie überraschen könnte, ist, dass wir die Eigenschaft val (unveränderlich) mit var (veränderlich) überschreiben können.

Stellen Sie sicher, dass Sie diese Funktionalität mit Bedacht einsetzen! Beachten Sie, dass wir das Gegenteil nicht tun können - überschreiben Sie eine var-Eigenschaft mit val.

2. Schnittstellen

Eine Schnittstelle ist einfach eine Sammlung verwandter Methoden, mit denen Sie Objekten normalerweise mitteilen können, was zu tun ist und wie dies standardmäßig zu tun ist. (Standardmethoden in Schnittstellen sind eine neue Funktion, die Java 8 hinzugefügt wurde.) Mit anderen Worten, eine Schnittstelle ist ein Vertrag, den implementierende Klassen einhalten müssen.

Eine Schnittstelle wird mit dem Schlüsselwort interface in Kotlin (ähnlich wie Java) definiert.

Im obigen Code haben wir eine StudentRepository-Schnittstelle deklariert. Diese Schnittstelle enthält zwei abstrakte Methoden: getById() und getResultsById(). Beachten Sie, dass das Einschließen des abstrakt Schlüsselworts in eine Schnittstellenmethode redundant ist, da sie bereits implizit abstrakt sind.

Eine Schnittstelle ist ohne einen oder mehrere Implementierer nutzlos. Erstellen wir also eine Klasse, die diese Schnittstelle implementiert.

Hier haben wir eine Klasse StudentLocalDataSource erstellt, die die StudentRepository-Schnittstelle implementiert.

Wir verwenden den override-Modifikator, um die Methoden und Eigenschaften zu kennzeichnen, die wir über die Schnittstelle oder Oberklasse neu definieren möchten. Dies ähnelt der Annotation @Override in Java.

Beachten Sie die folgenden zusätzlichen Schnittstellenregeln in Kotlin:

  • Eine Klasse kann so viele Schnittstellen implementieren, wie Sie möchten, sie kann jedoch nur eine einzelne Klasse erweitern (ähnlich wie Java).
  • Der override-Modifikator ist in Kotlin - anders als in Java - obligatorisch.
  • Neben Methoden können wir auch Eigenschaften in einer Kotlin-Schnittstelle deklarieren.
  • Eine Kotlin-Schnittstellenmethode kann eine Standardimplementierung haben (ähnlich wie Java 8).

Sehen wir uns ein Beispiel für eine Schnittstellenmethode mit einer Standardimplementierung an.

Im vorhergehenden Code haben wir eine neue Methode delete() mit einer Standardimplementierung hinzugefügt (obwohl ich den eigentlichen Implementierungscode zu Demonstrationszwecken nicht hinzugefügt habe).

Wir haben auch die Freiheit, die Standardimplementierung zu überschreiben, wenn wir wollen.

Wie bereits erwähnt, kann eine Kotlin-Schnittstelle Eigenschaften haben. Beachten Sie jedoch, dass der Status nicht beibehalten werden kann. (Denken Sie jedoch daran, dass abstrakte Klassen den Status beibehalten können.) Die folgende Schnittstellendefinition mit einer Eigenschaftsdeklaration funktioniert also.

Wenn wir jedoch versuchen, der Schnittstelle einen Status hinzuzufügen, indem wir der Eigenschaft einen Wert zuweisen, funktioniert dies nicht.

Eine Interface-Eigenschaft in Kotlin kann jedoch Getter- und Setter-Methoden haben (allerdings nur letztere, wenn die Eigenschaft veränderbar ist). Beachten Sie auch, dass die Eigenschaft in einer Schnittstelle kein Hintergrundfeld haben kann.

Wenn Sie möchten, können Sie auch eine Schnittstelleneigenschaft überschreiben, um sie neu zu definieren.

Schauen wir uns einen Fall an, in dem eine Klasse mehrere Schnittstellen mit derselben Methodensignatur implementiert. Wie entscheidet die Klasse, welche Schnittstellenmethode aufgerufen werden soll?

Hier haben wir zwei Schnittstellen, die eine Methode mit derselben Signatur funD() haben. Erstellen wir eine Klasse, die diese beiden Schnittstellen implementiert und die Methode funD() überschreibt.

Der Compiler ist verwirrt über den Aufruf der Methode super.funD(), da die beiden von der Klasse implementierten Schnittstellen dieselbe Methodensignatur haben.

Um dieses Problem zu lösen, setzen wir den Schnittstellennamen, für den wir die Methode aufrufen möchten, in spitze Klammern <InterfaceName>. (IntelliJ IDEA oder Android Studio geben Ihnen einen Hinweis zur Lösung dieses Problems, wenn es auftritt.)

Hier rufen wir die funD()-Methode von InterfaceA auf. Problem gelöst!

3. Vererbung

Eine neue Klasse (Unterklasse) wird erstellt, indem die Mitglieder einer vorhandenen Klasse (Oberklasse) erfasst und möglicherweise ihre Standardimplementierung neu definiert werden. Dieser Mechanismus wird in der objektorientierten Programmierung(OOP) als Vererbung bezeichnet. Eines der Dinge, die Kotlin so großartig machen, ist, dass es sowohl das OOP- als auch das funktionale Programmierparadigma umfasst - alles in einer Sprache.

Die Basisklasse für alle Klassen in Kotlin ist Any.

Der Any-Typ entspricht dem Object-Typ, den wir in Java haben.

Der Any-Typ enthält die folgenden Elemente: equals(), hashcode() und auch toString()-Methoden (ähnlich wie Java).

Unsere Klassen müssen diesen Typ nicht explizit erweitern. Wenn Sie nicht explizit angeben, welche Klasse eine neue Klasse erweitert, erweitert die Klasse Any implizit. Aus diesem Grund müssen Sie normalerweise : Any nicht in Ihren Code aufnehmen - wir tun dies zu Demonstrationszwecken im obigen Code.

Lassen Sie uns nun die Erstellung von Klassen in Kotlin unter Berücksichtigung der Vererbung untersuchen.

Im obigen Code erweitert die GraduateStudent-Klasse die Superklasse Student. Dieser Code wird jedoch nicht kompiliert. Warum? Weil Klassen und Methoden in Kotlin standardmäßig final sind. Mit anderen Worten, sie können nicht standardmäßig erweitert werden - anders als in Java, wo Klassen und Methoden standardmäßig geöffnet sind.

Die bewährte Methode für die Softwareentwicklung empfiehlt, dass Sie damit beginnen, Ihre Klassen und Methoden standardmäßig final zu machen, d. h. wenn sie nicht speziell dazu bestimmt sind, in Unterklassen neu definiert oder überschrieben zu werden. Das Kotlin-Team(JetBrains) hat diese Codierungsphilosophie und viele weitere Best Practices für die Entwicklung dieser modernen Sprache angewendet.

Damit Unterklassen aus einer Oberklasse erstellt werden können, müssen wir die Oberklasse explizit mit dem Modifikator open markieren. Dieser Modifikator gilt auch für alle Eigenschaften oder Methoden der Oberklasse, die von Unterklassen überschrieben werden sollen.

Wir setzen einfach den open Modifikator vor das Schlüsselwort class. Wir haben jetzt den Compiler angewiesen, unsere student-Klasse für Erweiterungen offen zu lassen.

Wie bereits erwähnt, sind Mitglieder einer Kotlin-Klasse standardmäßig ebenfalls endgültig.

Im vorhergehenden Code haben wir die Funktion schoolFees als open markiert, damit Unterklassen sie überschreiben können.

Hier wird die offene schoolFees-Funktion der Superklasse Student von der GraduateStudent-Klasse überschrieben, indem der override-Modifikator vor dem Schlüsselwort fun hinzugefügt wird. Beachten Sie, dass beim Überschreiben eines Mitglieds einer Oberklasse oder Schnittstelle das überschreibende Mitglied standardmäßig ebenfalls open ist, wie im folgenden Beispiel:

Auch wenn wir die schoolFees()-Methode in der GraduateStudent-Klasse nicht mit dem open Modifikator markiert haben, können wir sie dennoch überschreiben - wie in der ComputerScienceStudent-Klasse. Um dies zu verhindern, müssen wir das übergeordnete Mitglied als final markieren.

Denken Sie daran, dass wir einer Klasse durch die Verwendung von Erweiterungsfunktionen in Kotlin neue Funktionen hinzufügen können - auch wenn diese endgültig sind. Eine Auffrischung der Erweiterungsfunktionen finden Sie in meinem Beitrag Erweiterte Funktionen in Kotlin. Wenn Sie eine Auffrischung benötigen, wie Sie selbst einer endgültigen Klasse neue Eigenschaften zuweisen können, ohne davon zu erben, lesen Sie den Abschnitt über Erweiterungseigenschaften in meinem Beitrag Erweiterte Eigenschaften und Klassen.

Wenn unsere Oberklasse einen primären Konstruktor wie diesen hat:

Dann muss jede Unterklasse den primären Konstruktor der Oberklasse aufrufen.

Wir können einfach wie gewohnt ein Objekt der GraduateStudent-Klasse erstellen:

Wenn die Unterklasse den Superklassenkonstruktor von ihrem Sekundärkonstruktor aus aufrufen möchte, verwenden wir das Schlüsselwort super (ähnlich wie Superklassenkonstruktoren in Java aufgerufen werden).

Wenn Sie eine Auffrischung zu Klassenkonstruktoren in Kotlin benötigen, besuchen Sie bitte meinen Beitrag zu Klassen und Objekten.

4. Bonus: Geben Sie Alias ein

Eine andere großartige Sache, die wir in Kotlin tun können, ist, einem Typ einen Alias zu geben.

Sehen wir uns ein Beispiel an.

In der obigen Klasse können wir die String- und Int-Typen für die Aliase der Person-Eneigenschaften mithilfe des typealias-Modifikators  in Kotlin zuweisen. Dieser Modifikator wird verwendet, um einen Alias eines beliebigen Typs in Kotlin zu erstellen - einschließlich der von Ihnen erstellten.

Wie Sie sehen können, haben wir einen Alias-Name und Age für den String- bzw. den Int-Typ erstellt. Wir haben jetzt den Eigenschaftstyp firstName und lastName durch unseren Alias-Name ersetzt - und auch den Int-Typ in den Alias Age. Beachten Sie, dass wir keine neuen Typen erstellt haben. Stattdessen haben wir einen Alias für die Typen erstellt.

Diese können nützlich sein, wenn Sie Typen in Ihrer Kotlin-Codebasis eine bessere Bedeutung oder Semantik verleihen möchten. Also benutze sie mit Bedacht!

Schlussfolgerung

In diesem Tutorial haben Sie mehr über objektorientierte Programmierung in Kotlin erfahren. Wir haben Folgendes behandelt:

  • abstrakte Klassen
  • Schnittstellen
  • Erbe
  • Typ Alias

Wenn Sie Kotlin über unsere Kotlin From Scratch-Reihe gelernt haben, stellen Sie sicher, dass Sie den angezeigten Code eingegeben und auf Ihrer IDE ausgeführt haben. Ein guter Tipp, um eine neue Programmiersprache (oder ein anderes Programmierkonzept), die Sie lernen, wirklich zu verstehen, ist sicherzustellen, dass Sie nicht nur die Lernressource oder den Leitfaden lesen, sondern auch den eigentlichen Code eingeben und ausführen!

Im nächsten Tutorial der Kotlin From Scratch-Reihe werden Sie in die Ausnahmebehandlung in Kotlin eingeführt. Bis bald!

Um mehr über die Kotlin-Sprache zu erfahren, empfehle ich, die Kotlin-Dokumentation zu besuchen. Oder sehen Sie sich einige unserer anderen Beiträge zur Entwicklung von Android-Apps hier auf Envato Tuts an!


Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.