Vererbung in Java: Klassen sinnvoll erweitern
Du lernst, wie Java-Klassen andere Klassen erweitern, welche Methode ausgeführt wird und wie austauschbare Objekte Verhalten verändern.
Kurz gesagt
Mit Vererbung erweitert eine Java-Klasse eine passende Basisklasse und kann deren Methoden überschreiben. Bei überschriebenen Instanzmethoden entscheidet das tatsächliche Objekt über die Ausführung. Komposition ermöglicht austauschbares Verhalten durch enthaltene Objekte.
Java ist eine Programmiersprache. Eine Klasse beschreibt einen Bauplan für Objekte, also einzelne Exemplare dieses Bauplans. Du untersuchst, wie Klassen gemeinsame Eigenschaften nutzen und wie du veränderbares Verhalten sinnvoll aufteilst.
Deine Lernziele
Eine Klasse legt Eigenschaften und Verhalten ihrer Objekte fest. Ein Attribut speichert eine Eigenschaft. Eine Methode beschreibt eine aufrufbare Folge von Anweisungen. Eine Instanzmethode gehört zu einem einzelnen Objekt; eine Instanz ist ein anderes Wort für ein Objekt.
Bei der Vererbung erweitert eine abgeleitete Klasse eine Basisklasse. Sie kann geerbte Methoden weiterverwenden, zusätzliche Eigenschaften erhalten und geeignetes Verhalten verändern. Als Begründung hilft die Beziehung „ist ein“: Jedes Objekt der abgeleiteten Klasse soll auch als Objekt der Basisklasse verwendbar sein und deren vereinbarte Aufgaben erfüllen. Gemeinsame Methoden müssen dann nicht erneut geschrieben werden. Dass zwei Klassen einige ähnliche Eigenschaften haben, reicht als Begründung noch nicht aus.
Welche Bestandteile vererbt werden, hängt von ihren Zugriffsregeln ab. Ein Paket ist eine Gruppe zusammengehörender Java-Klassen. Die Tabelle zeigt die Regeln. Private Daten der Basisklasse können weiterhin zum erzeugten Objekt gehören; die abgeleitete Klasse darf jedoch nicht direkt darauf zugreifen. Sie kann dafür zugängliche Methoden der Basisklasse nutzen. Ein Konstruktor bereitet ein neues Objekt vor. Konstruktoren werden nicht vererbt, können aber aus der abgeleiteten Klasse aufgerufen werden.
Basisklasse und abgeleitete Klasse
Die Basisklasse liefert den allgemeineren Bauplan. Die abgeleitete Klasse erweitert diesen Bauplan um passendere Eigenschaften oder Verhaltensweisen.
Beispiel: Ein Lieferwagen als Fahrzeug: ausgedachtes Modell
- 1Die Klasse Fahrzeug bietet die Methode fahren(). Die leeren Klammern bedeuten hier: Der Aufruf benötigt keine zusätzlichen Werte.
- 2Die Klasse Lieferwagen erweitert Fahrzeug und ergänzt das Attribut laderaum.
- 3Ein Lieferwagen ist ein Fahrzeug und erfüllt weiterhin dessen vereinbarte Aufgabe fahren(). Deshalb passt die Vererbung.
- 4Ein Lager enthält Fahrzeuge, ist aber selbst kein Fahrzeug. Das Enthaltensein begründet keine Vererbung von Fahrzeug.
| Kennzeichnung | Vererbung |
|---|---|
| public: öffentlich | Wird auch über Paketgrenzen hinweg vererbt. |
| protected: geschützt | Wird auch über Paketgrenzen hinweg vererbt. |
| Keine Zugriffskennzeichnung | Wird vererbt, wenn beide Klassen im selben Paket liegen. |
| private: privat | Wird nicht vererbt; zugängliche Methoden können den Zugriff auf private Daten ermöglichen. |
Teste dich
Das Java-Schlüsselwort extends nennt die Basisklasse: class Kuehltasche extends Tasche erklärt Kuehltasche zur abgeleiteten Klasse von Tasche. class beginnt eine Klassendefinition. Die zusammengehörenden Klassen unserer Java-Beispiele liegen jeweils im selben Paket. Die gezeigten Ausschnitte sind Java-Code, aber keine vollständigen Programme mit einem Programmstart.
Ein Konstruktor trägt den Namen seiner Klasse und hat keinen Rückgabetyp. Ein Rückgabetyp beschreibt, welche Art von Ergebnis eine Methode liefert. Mit super(...) ruft ein Konstruktor einen passenden Konstruktor der Basisklasse auf. Die Punkte stehen hier als Platzhalter für übergebene Werte und gehören nicht in den ausführbaren Aufruf. super() übergibt keine Werte; super(liter) übergibt den Wert von liter. Der Aufruf muss zu einem vorhandenen, zugänglichen Konstruktor passen.
Eine abgeleitete Klasse kann eine geerbte Instanzmethode überschreiben, also eine eigene Ausführung für sie bereitstellen. Dafür müssen Methodenname sowie Anzahl und Typen der Parameter übereinstimmen. Parameter sind benannte Eingabewerte. In unseren Beispielen bleibt auch der Rückgabetyp gleich. Bei Rückgaben von Objekten ist außerdem ein speziellerer Typ zulässig. Die Zugriffsrechte dürfen beim Überschreiben erweitert, aber nicht eingeschränkt werden. Eine mit static gekennzeichnete Methode gehört zur Klasse statt zu einem einzelnen Objekt; zwischen einer solchen Klassenmethode und einer Instanzmethode darf beim Überschreiben nicht gewechselt werden.
@Override ist eine Prüfkennzeichnung für den Compiler, das Übersetzungsprogramm für Java-Code. Sie kündigt an, dass die Methode eine vorhandene Methode überschreiben soll. Passt die Methode nicht zu einer entsprechenden geerbten Methode, meldet der Compiler einen Fehler. Die Kennzeichnung erzeugt die Vererbungsbeziehung nicht selbst.
Im folgenden Code bedeutet int eine ganze Zahl und String eine Zeichenfolge. Ein Gleichheitszeichen weist einen Wert zu. Runde Klammern enthalten Parameter oder übergebene Werte. Geschweifte Klammern umschließen die Bestandteile einer Klasse oder die Anweisungen eines Konstruktors oder einer Methode. Ein Semikolon beendet hier eine Anweisung. return liefert das Ergebnis der Methode zurück. Die geraden Anführungszeichen gehören zur Java-Schreibweise für Zeichenfolgen.
Beispiel: Tasche und Kuehltasche: ausgedachter Java-Code
- 1class Tasche {
- 2 int volumen;
- 3 Tasche(int liter) { volumen = liter; }
- 4 String hinweis() { return "Tragen"; }
- 5}
- 6class Kuehltasche extends Tasche {
- 7 Kuehltasche(int liter) { super(liter); }
- 8 @Override
- 9 String hinweis() { return "Kühl halten"; }
- 10}
Merke: extends legt die Basisklasse fest. super(liter) ruft deren passenden Konstruktor auf. @Override lässt die beabsichtigte Überschreibung prüfen. Erhält der Konstruktor von Kuehltasche für liter den Wert 12, speichert der aufgerufene Tasche-Konstruktor im Attribut volumen den Wert 12.
Teste dich
Eine Variable ist ein benannter Speicherplatz. Eine Variable für Objekte speichert einen Verweis auf ein Objekt. Der Verweis ermöglicht den Zugriff auf dieses Objekt. Der Variablentyp legt fest, welche Art von Objekt die Variable aufnehmen darf und welche Methoden über sie aufrufbar sind. Eine Variable vom Typ einer Basisklasse kann auch auf ein Objekt einer passenden abgeleiteten Klasse verweisen.
Polymorphie bedeutet hier: Über denselben Basisklassentyp können unterschiedliche passende Objekte angesprochen werden. Bei einem Aufruf einer überschriebenen Instanzmethode richtet sich die ausgeführte Methode nach dem tatsächlichen Objekt. Der Typ der Variablen entscheidet dabei nicht, welche Überschreibung ausgeführt wird. Hat die tatsächliche Klasse keine eigene Überschreibung, kann eine geerbte Ausführung verwendet werden. Diese Auswahlregel bezieht sich auf Instanzmethoden, nicht auf Attribute oder mit static gekennzeichnete Klassenmethoden.
Im folgenden ausgedachten Beispiel besitzt Nachricht die Instanzmethode String text(), die „Meldung“ zurückgibt. Eilmeldung erbt von Nachricht und überschreibt dieselbe Methode; ihre Ausführung gibt „Eilig“ zurück. Beide Klassen haben einen aufrufbaren Konstruktor ohne Parameter. new erzeugt ein neues Objekt und ruft dessen Konstruktor auf. Der Punkt in n.text() bedeutet, dass die Methode über den in n gespeicherten Objektverweis aufgerufen wird.
Beispiel: Eine Eilmeldung über eine Nachricht-Variable ansprechen
- 1Nachricht n = new Eilmeldung();
- 2Der Variablentyp ist Nachricht. Das erzeugte Objekt gehört zur Klasse Eilmeldung.
- 3n.text();
- 4Dieser Aufruf liefert die Zeichenfolge „Eilig“, weil Eilmeldung die Methode überschreibt.
- 5Ein Rückgabewert wird dadurch noch nicht auf dem Bildschirm ausgegeben.
Merke: Trenne Variablentyp und tatsächliches Objekt. Für die Auswahl einer überschriebenen Instanzmethode ist das tatsächliche Objekt maßgeblich.
Teste dich
Bei Komposition im hier verwendeten Sinn enthält eine Klasse Verweise auf andere Objekte und nutzt deren Fähigkeiten. Die Beziehung lautet „hat ein“: Eine Spielfigur hat beispielsweise ein Objekt, das ihre Bewegung bestimmt. Sie muss dafür nicht von diesem Bewegungsobjekt erben.
Eine Schnittstelle beschreibt gemeinsame vereinbarte Methoden, die verschiedene Klassen bereitstellen. Die nutzende Klasse kann ihren Verweis über diesen gemeinsamen Typ halten. Sie ruft die vereinbarte Methode auf, ohne die einzelnen Ausführungsdetails kennen zu müssen. Die beteiligten Objekte müssen diese Vereinbarung erfüllen.
Zur Laufzeit bedeutet: während das Programm läuft. Ist der gespeicherte Verweis ausdrücklich austauschbar, kann die Klasse während des Programmlaufs ein anderes passendes Verhaltensobjekt erhalten. Weitere Aufrufe nutzen dann dessen Verhalten. Der Klassenname und die durch extends festgelegte Basisklasse des nutzenden Objekts ändern sich bei diesem Austausch nicht. Komposition macht Verhalten also nicht von selbst austauschbar; die Austauschmöglichkeit muss vorgesehen sein.
Ein Vorteil ist, dass du Verhalten getrennt weiterentwickeln kannst. Bleibt die gemeinsame Schnittstelle erhalten, sind dafür oft keine Änderungen an der nutzenden Klasse nötig. Ändert sich die Vereinbarung, können auch dort Anpassungen nötig werden. Zudem muss die nutzende Klasse ihre Aufrufe an das enthaltene Objekt weiterreichen; diese Verbindung muss programmiert werden. Vererbung bleibt sinnvoll, wenn eine passende „ist ein“-Beziehung und die vereinbarten Aufgaben der Basisklasse erhalten bleiben.
Beispiel: Eine Spielfigur erhält eine andere Bewegungsregel
- 1Dies ist eine ausgedachte Modellbeschreibung, kein ausführbarer Java-Code.
- 2Eine Spielfigur speichert einen austauschbaren Verweis auf ein Bewegungsobjekt.
- 3Die Bewegungsobjekte für Gehen und Springen bieten beide die vereinbarte Methode bewege().
- 4Zunächst verweist die Spielfigur auf das Gehobjekt und reicht Bewegungsaufrufe an dieses weiter.
- 5Während das Programm läuft, wird der Verweis auf ein Sprungobjekt gesetzt. Weitere Bewegungsaufrufe nutzen dieses Objekt.
- 6Die Spielfigur behält dabei ihre Klasse.
Merke: Komposition kann Verhalten zur Laufzeit austauschbar machen, wenn ein austauschbarer Verweis und eine gemeinsame Schnittstelle vorgesehen sind. Änderungen bleiben nicht in jedem Fall ohne Folgen für die nutzende Klasse.
Teste dich
Alles auf einen Blick
Vererbung (Programmierung)
Vererbung passt zu einer „ist ein“-Beziehung, bei der die vereinbarten Aufgaben der Basisklasse erhalten bleiben.
Private Bestandteile und Konstruktoren werden nicht vererbt; zugängliche Methoden können private Daten nutzbar machen.
extends nennt die Basisklasse, super(...) ruft deren passenden Konstruktor auf und @Override lässt eine Überschreibung prüfen.
Bei überschriebenen Instanzmethoden bestimmt das tatsächliche Objekt die ausgeführte Methode.
Enthaltene Verhaltensobjekte lassen sich während des Programmlaufs wechseln, wenn ein austauschbarer Verweis vorgesehen ist.
Eine gemeinsame Schnittstelle kann Änderungen begrenzen, garantiert aber keine vollständige Unabhängigkeit.
Einen Methodenaufruf bei einem Frostsensor verfolgen
- 1Ausgedachte Voraussetzung: Sensor besitzt die Instanzmethode String meldung(), die „Messung“ zurückgibt.
- 2Frostsensor erbt von Sensor und überschreibt dieselbe Methode mit dem Rückgabewert „Frost“. Beide Klassen haben aufrufbare Konstruktoren ohne Parameter.
- 3Der Java-Ausschnitt beginnt mit Sensor s = new Frostsensor();. Der Variablentyp ist Sensor; das tatsächliche Objekt gehört zur Klasse Frostsensor.
- 4Danach wird String ergebnis = s.meldung(); ausgeführt. Der Aufruf betrifft eine überschriebene Instanzmethode.
- 5Deshalb wird die Ausführung von Frostsensor gewählt. Die Variable ergebnis enthält anschließend die Zeichenfolge „Frost“.
- 6Der Ausschnitt speichert einen Rückgabewert. Er enthält keine Anweisung für eine Bildschirmausgabe.
Fehler finden
Fehler in Zeile 4Die falsche Zeile wählt den Rückgabewert nach dem Variablentyp. Richtig: g.wort() liefert „Moin“, weil g auf ein Morgengruss-Objekt verweist und dessen Überschreibung ausgeführt wird.
Lückentext
Bei super(4) wird der passende zugängliche Konstruktor der aufgerufen. Bei einer überschriebenen Instanzmethode richtet sich die Auswahl der Ausführung nach dem . Eine Klasse hält einen ausdrücklich austauschbaren Verweis auf ein Verhaltensobjekt. Beim vorgesehenen Wechsel wird .