Informatik

Vererbung in Java: Klassen sinnvoll erweitern

Vererbung in Java: Klassen sinnvoll erweitern

Vererbung in Java: Klassen sinnvoll erweitern
Vererbung in Java: Klassen sinnvoll erweitern
Für Quiz, Lückentext, Lernkarten und Fortschritt ist JavaScript nötig. Alle Inhalte und Lösungen bleiben direkt lesbar.

Bei der Vererbung entsteht aus einer allgemeinen Klasse eine speziellere Klasse. Die Unterklasse übernimmt verwendbare Merkmale der Oberklasse, kann neue ergänzen und Methoden passend überschreiben. Sinnvoll ist das nur, wenn wirklich eine Ist-eine-Art-von-Beziehung besteht und Objekte der Unterklasse die Erwartungen an die Oberklasse erfüllen.

Deine Lernziele

Hake ab, was du schon kannst — und komm am Ende hierher zurück!

Was wird bei der Vererbung übernommen?

Zur Erinnerung: Eine Klasse beschreibt den Aufbau und das Verhalten ihrer Objekte. Ein Attribut speichert Daten, eine Methode führt eine Aktion aus.

Stell dir ein fiktives Inventarsystem vor. Jedes Produkt besitzt eine Artikelnummer, einen Preis und einen Hersteller. Ein Buch ist eine besondere Art von Produkt und ergänzt Autor und ISBN.

Definition

Vererbung

Vererbung ist eine dauerhafte Beziehung zwischen Klassen. Eine Unterklasse wird aus einer allgemeineren Oberklasse abgeleitet. Sie übernimmt deren zugängliches Verhalten und kann die Klasse spezialisieren.

Für das Beispiel gilt:

  • Produkt ist die Oberklasse.
  • Buch ist die Unterklasse.
  • Ein Buch ist ein Produkt.
  • Ein Buch übernimmt allgemeine Produktmerkmale und ergänzt buchtypische Merkmale.

Eine Unterklasse kann auf drei Arten spezialisiert werden:

  1. übernehmen: Eine geerbte Methode unverändert verwenden,
  2. ergänzen: neue Attribute oder Methoden hinzufügen,
  3. überschreiben: eine geerbte Methode mit passendem Spezialverhalten neu implementieren.

Vererbung wirkt über mehrere Stufen. Wenn Dungeon von Level und Level von GameObject erbt, ist ein Dungeon auch ein GameObject. Eine solche Kette heißt mehrstufige Vererbung.

Merke

Frage zuerst: „Ist jedes Objekt der Unterklasse auch sinnvoll als Objekt der Oberklasse verwendbar?“ Ein gemeinsames Merkmal allein reicht für Vererbung nicht aus.

Teste dich
Frage 1 von 2LeichtWelche Aussage beschreibt eine passende Vererbung?
Lösung: Ein Buch ist eine besondere Art von Produkt. — Eine Unterklasse muss eine besondere Art der Oberklasse sein. Bestandteile und Eigenschaften werden gewöhnlich nicht als Oberklassen modelliert.
Frage 2 von 2MittelSparkonto erbt von Konto. Was kann Sparkonto sinnvoll tun?
Lösung: Allgemeine Kontofunktionen übernehmen und eine Zinsfunktion ergänzen. — Eine Unterklasse kann Verhalten übernehmen, ergänzen und überschreiben. Konstruktoren selbst werden in Java nicht vererbt.
Wie wird eine Unterklasse in Java geschrieben?

Java verwendet das Schlüsselwort extends. Die Grundform lautet: class Unterklasse extends Oberklasse { ... }.

Beispiel

Die Oberklasse Produkt stellt die Methode beschreibung() bereit. Die Unterklasse wird mit class Buch extends Produkt { ... } deklariert. Ein Buch kann die geerbte Methode verwenden und zusätzlich etwa das Attribut isbn sowie eine Methode autorAnzeigen() besitzen.

Gemeinsame Produktlogik bleibt dadurch in Produkt. Nur die buchtypischen Ergänzungen stehen in Buch.

Eine Java-Klasse hat höchstens eine direkte Oberklasse. Mehrere gemeinsame Verträge können über Interfaces beschrieben werden: Ein Interface legt beispielsweise fest, welche Methoden eine Klasse anbieten muss. Eine Klasse kann mehrere Interfaces mit implements umsetzen. Das ist von mehreren direkten Oberklassen zu unterscheiden.

Die Sichtbarkeit bestimmt, worauf anderer Code zugreifen darf. Für unsere Beispiele verwenden wir eigenständige, nicht ineinander verschachtelte Klassen. Ein Paket fasst Klassen unter einem gemeinsamen Namen zusammen.

  • public: zugänglich, sofern auch die Klasse zugänglich ist,
  • protected: im selben Paket und unter besonderen Regeln auch aus Unterklassen anderer Pakete zugänglich,
  • ohne Zugriffsmodifikator: nur innerhalb desselben Pakets zugänglich,
  • private: in unseren eigenständigen Klassen nur innerhalb der deklarierenden Klasse direkt zugänglich.

Außerhalb des Pakets darf eine Unterklasse auf geschützte Instanzmitglieder über this oder über eine Referenz ihres eigenen Typs beziehungsweise eines weiteren Untertyps zugreifen. Eine beliebige Referenz vom Oberklassentyp genügt dafür nicht.

Private Mitglieder werden in Java nicht vererbt. Trotzdem enthält ein Unterklassenobjekt auch die privaten Instanzattribute seiner Oberklasse. Die eigenständige Unterklasse greift darauf bei Bedarf über geeignete Methoden der Oberklasse zu. Bei verschachtelten Klassen gelten zusätzliche Zugriffsrechte innerhalb der gemeinsamen äußersten Klasse.

Lückentext

Wähle in jeder Lücke die passende Form und prüfe anschließend deine Antworten.

In Java verbindet das Schlüsselwort eine Unterklasse mit ihrer Oberklasse. Ein vor direktem Zugriff aus einer eigenständigen Unterklasse geschütztes Attribut ist . Eine Unterklasse kann eine geerbte Methode , wenn die Methode dies erlaubt.

Lösungen: Lücke 1: extends; Lücke 2: private; Lücke 3: überschreiben. extends bezeichnet Klassenvererbung. private verhindert in unseren eigenständigen Klassen den direkten Zugriff von außen und aus Unterklassen. Beim Überschreiben erhält eine geerbte Methode eine passende neue Implementierung.
Wie funktionieren Überschreiben und Polymorphie?

Beim Überschreiben implementiert die Unterklasse eine Instanzmethode der Oberklasse neu. Eine Instanzmethode wird für ein bestimmtes Objekt aufgerufen. In unseren einfachen Beispielen müssen Methodenname sowie Anzahl, Reihenfolge und Typen der Parameter übereinstimmen; dies beschreibt die Signatur. Auch der Rückgabetyp muss kompatibel sein, und die Sichtbarkeit darf nicht eingeschränkt werden.

@Override lässt den Java-Compiler prüfen, ob die Methode tatsächlich eine Methode eines Obertyps überschreibt oder umsetzt. Die Annotation erzeugt die Überschreibung nicht erst. Eine final-Methode darf nicht überschrieben werden. static-Methoden gehören zur Klasse und werden bei gleichem Namen und gleicher Signatur verdeckt, nicht dynamisch überschrieben.

Definition

Polymorphie

Polymorphie bedeutet hier: Eine Variable des Oberklassentyps kann auf Objekte verschiedener Unterklassen verweisen. Beim Aufruf einer überschriebenen Instanzmethode wird die zum konkreten Objekt passende Implementierung ausgeführt.

Beispiel

In einem fiktiven Zeichenprogramm legt die abstrakte Klasse Shape die Methode draw() fest. Abstrakt bedeutet: Du kannst kein Objekt direkt mit new Shape() erzeugen. Die konkreten Unterklassen Rectangle und Circle implementieren draw() jeweils. Beide besitzen hier einen zugänglichen parameterlosen Konstruktor.

So liest du die Aufrufe Schritt für Schritt:

  • Shape s1 = new Rectangle();
  • Shape s2 = new Circle();
  • s1.draw(); führt die Implementierung aus Rectangle aus.
  • s2.draw(); führt die Implementierung aus Circle aus.

Der deklarierte Variablentyp ist in beiden Fällen Shape. Das konkrete Objekt entscheidet zur Laufzeit über die ausgeführte überschreibende Methode. Dies nennt man dynamische Methodenbindung.

Über eine Variable des Oberklassentyps sind nur die dort bekannten und an der Aufrufstelle zugänglichen Mitglieder direkt erreichbar. Unterklassenspezifische Methoden werden dadurch nicht automatisch sichtbar. Ein Upcast von der Unterklasse zur Oberklasse ist dennoch möglich, weil das Unterklassenobjekt als Oberklassenobjekt verwendbar sein soll.

Teste dich
Frage 1 von 3LeichtWelche Annotation kennzeichnet in Java eine überschriebene Methode?
Lösung: @Override — @Override hilft, Tippfehler und unpassende Methodensignaturen beim Überschreiben zu erkennen.
Frage 2 von 3MittelShape form = new Circle(); wird ausgeführt. Circle überschreibt draw(). Welche Methode läuft bei form.draw()?
Lösung: Die draw()-Implementierung aus Circle. — Bei dynamischer Methodenbindung wird die zum konkreten Objekt passende überschriebene Methode aufgerufen.
Frage 3 von 3SchwerWarum muss eine Unterklasse das erwartete Verhalten der Oberklasse bewahren?
Lösung: Damit sie überall eingesetzt werden kann, wo die Oberklasse erwartet wird. — Eine echte Subtypbeziehung verlangt mehr als ähnliche Struktur: Verwender der Oberklasse dürfen durch das Verhalten der Unterklasse nicht unerwartet scheitern.
Wie wird der Oberklassenteil konstruiert?

Ein Konstruktor initialisiert ein neu erzeugtes Objekt. Er trägt den Klassennamen und hat keinen Rückgabetyp. Konstruktoren werden in Java nicht vererbt. Beim Erzeugen eines Unterklassenobjekts muss jedoch auch sein Oberklassenteil initialisiert werden. Mit super(...) ruft der Unterklassenkonstruktor dafür einen passenden Oberklassenkonstruktor auf. Es entsteht dabei ein einziges Unterklassenobjekt, kein zusätzliches eigenständiges Oberklassenobjekt.

Beispiel

In einem fiktiven Spiel besitzt GameObject den Konstruktor GameObject(String typ). Level erbt von GameObject und ergänzt die Zahl stufe.

Der Konstruktor von Level folgt diesem Ablauf:

  1. Level(String typ, int stufe) wird aufgerufen.
  2. super(typ) initialisiert zuerst den GameObject-Teil.
  3. Danach setzt der Unterklassenkonstruktor sein eigenes Attribut stufe.

Für dieses Beispiel schreiben wir den Konstruktor so: Level(String typ, int stufe) { super(typ); this.stufe = stufe; }. Dabei bezeichnet this.stufe das Attribut des neuen Objekts, stufe rechts vom Gleichheitszeichen den übergebenen Parameter.

Hier steht super(typ) zuerst. In Java 25 dürfen davor auch bestimmte Anweisungen stehen, etwa Prüfungen von Parametern. Die pauschale Regel „super(...) muss immer die erste Anweisung sein“ gilt daher nicht versionsübergreifend. Die hier gezeigte Schreibweise funktioniert auch in älteren Java-Versionen.

Fehlt sowohl ein ausdrückliches super(...) als auch this(...), beginnt der Konstruktor implizit mit super(). Dann muss ein zugänglicher parameterloser Oberklassenkonstruktor existieren. Mit this(...) kann ein Konstruktor zunächst an einen anderen Konstruktor derselben Klasse weiterleiten; diese Kette muss schließlich einen passenden Oberklassenkonstruktor erreichen.

Eine überschreibende Methode kann mit super.methodenname(...) gezielt eine zugängliche konkrete Implementierung aus der Oberklassenhierarchie aufrufen. Die Suche beginnt bei der direkten Oberklasse; die dort gefundene Methode kann auch von einer weiteren Oberklasse stammen. So kann sie allgemeines Verhalten beibehalten und anschließend Spezialverhalten ergänzen.

Teste dich
Frage 1 von 1MittelDie Oberklasse besitzt nur den zugänglichen Konstruktor Produkt(int nummer). Der Unterklassenkonstruktor erhält nummer und verwendet kein this(...). Was braucht er?
Lösung: Einen passenden ausdrücklichen Aufruf wie super(nummer). — Ohne ausdrücklichen Konstruktoraufruf würde Java super() einsetzen. Weil Produkt() nicht existiert, entsteht ein Compilerfehler. super(nummer) passt dagegen zum vorhandenen Konstruktor.
Wann ist Vererbung eine gute Modellierung?

Eine sprachlich plausible Einteilung ist noch keine gute Klassenhierarchie. Prüfe zwei Bedingungen:

  1. Ist-ein-Test: Ist die Unterklasse wirklich eine besondere Art der Oberklasse?
  2. Austauschbarkeit: Verhält sich jedes Unterklassenobjekt so, wie Verwender der Oberklasse es erwarten?
Definition

Komposition

Bei der Objektkomposition wird ein Objekt aus zusammenarbeitenden Objekten aufgebaut. Es hält beispielsweise eine Referenz auf ein anderes Objekt und nutzt dessen Methoden. So lässt sich eine Hat-Beziehung modellieren, ohne daraus eine Unterart zu machen. Gemeint ist hier der allgemeine Programmieransatz; in UML-Klassendiagrammen hat „Komposition“ eine engere Bedeutung mit zusätzlichen Regeln für die Zugehörigkeit der Teile.

Apfelkuchen ist ein Kuchen, aber kein Apfel. Die Zutaten gehören als Bestandteile zum Kuchen. Eine Vererbung von Apfel wäre deshalb unpassend.

Auch wechselnde Rollen sprechen gegen starre Unterklassen. Eine Person kann gleichzeitig Kunde und Mitarbeiter sein oder ihre Rolle wechseln. Verknüpfte Rollenobjekte bilden das flexibler ab als immer neue Klassen wie KundeUndMitarbeiter.

Gut zu wissen

Das bekannte Modell Quadrat extends Rechteck wirkt zunächst logisch. Es kann aber problematisch werden, sobald ein veränderbares Rechteck Länge und Breite unabhängig setzen lässt. Gleicht ein Quadrat beim Setzen der Länge automatisch auch die Breite an, verhält es sich anders als ein Verwender eines allgemeinen Rechtecks erwartet. Das zeigt: Mathematische Einordnung allein garantiert noch keine sichere Subtypbeziehung im Programm.

Tiefe Hierarchien und unnötiges Überschreiben können Änderungen erschweren. Ändert sich eine zentrale Oberklasse, können viele Unterklassen betroffen sein. Halte Hierarchien deshalb überschaubar, kapsle interne Daten und beschreibe das erwartete Verhalten von Methoden eindeutig.

Beispiel

In einem fiktiven Geometrieprogramm kann die Klassenfolge Viereck → Rechteck → Quadrat gemeinsame Berechnungen nutzen. Ein Rechteck besitzt je zwei gleich lange gegenüberliegende Seiten. Deshalb ergibt sich bei Länge 10 cm und Breite 20 cm der Umfang aus 2 · 10 + 2 · 20 = 60, also 60 cm. Für die Fläche multiplizierst du Länge und Breite: 10 · 20 = 200, also 200 cm².

Beim Quadrat sind alle vier Seiten gleich lang. Bei Seitenlänge 30 cm ergibt sich der Umfang aus 4 · 30 = 120, also 120 cm; die Fläche aus 30² = 900, also 900 cm². Die Rechnungen zeigen mögliche Wiederverwendung. Ob die Vererbungsbeziehung auch bei veränderbaren Seiten korrekt bleibt, muss zusätzlich am erwarteten Verhalten geprüft werden.

Teste dich
Frage 1 von 3LeichtWelche Beziehung spricht für Komposition statt Vererbung?
Lösung: Ein Auto hat einen Motor. — „Hat ein“ beschreibt einen Bestandteil oder eine Zusammenarbeit. Dafür ist Komposition meist geeigneter.
Frage 2 von 3MittelEine Unterklasse besitzt ähnliche Attribute wie die Oberklasse, verletzt aber deren Verhaltensregeln. Was folgt daraus?
Lösung: Die Vererbungsbeziehung ist trotz der Ähnlichkeit ungeeignet. — Strukturähnlichkeit reicht nicht. Ein Unterklassenobjekt darf Verwender des Oberklassentyps nicht durch unvereinbares Verhalten überraschen.
Frage 3 von 3SchwerEine Person kann gleichzeitig Kunde und Mitarbeiter sein. Welche Modellierung ist am flexibelsten?
Lösung: Die Person wird mit getrennten Rollenobjekten verknüpft. — Mehrere oder wechselnde Rollen werden besser als Beziehungen modelliert als durch eine starre Vererbungshierarchie.
Karteikasten
Karteikasten

Überlege zuerst selbst und drehe die Karte anschließend zum Prüfen um.

Alles auf einen Blick
Mindmap
  • Vererbung
    • Beziehung
      • Oberklasse und Unterklasse
      • Ist-eine-Art-von
      • Austauschbarkeit
    • Java
      • `extends`
      • `super(...)`
      • `@Override`
    • Spezialisierung
      • übernehmen
      • ergänzen
      • überschreiben
    • Laufzeit
      • Polymorphie
      • dynamische Methodenbindung
    • Entwurfsprüfung
      • Verhalten bewahren
      • Komposition bei Hat-Beziehungen
      • Hierarchien überschaubar halten
Abschluss-Check
Teste dich
Frage 1 von 5LeichtWelche Formulierung trifft Vererbung am besten?
Lösung: Eine speziellere Klasse übernimmt und verändert bei Bedarf Merkmale einer allgemeineren Klasse. — Vererbung verbindet Ober- und Unterklasse dauerhaft und drückt eine fachlich passende Unterart aus.
Frage 2 von 5MittelWas geschieht bei Oberklasse x = new Unterklasse(); x.methode();, wenn die Unterklasse methode() korrekt überschreibt?
Lösung: Die überschriebene Implementierung der konkreten Unterklasse wird ausgeführt. — Das konkrete Objekt bestimmt bei einer überschriebenen Instanzmethode die ausgeführte Implementierung.
Frage 3 von 5MittelWarum kann ein Unterklassenkonstruktor super(argument) benötigen?
Lösung: Weil der Oberklassenteil mit einem passenden Oberklassenkonstruktor initialisiert werden muss. — Fehlt ein zugänglicher parameterloser Oberklassenkonstruktor, muss die Konstruktorverkettung schließlich ausdrücklich einen passenden Oberklassenkonstruktor aufrufen. Eine Weiterleitung innerhalb der Unterklasse mit this(...) ist möglich.
Frage 4 von 5SchwerEin Team möchte Drucker extends Tinte modellieren, weil ein Drucker Tinte verwendet. Wie sollte es den Entwurf verbessern?
Lösung: Der Drucker sollte ein Tintenobjekt enthalten oder verwenden. — Ein Drucker hat Tinte, ist aber keine Tinte. Diese Beziehung wird durch Komposition dargestellt.
Frage 5 von 5SchwerWelche Prüfung ist vor dem Ergänzen einer neuen Unterklasse am wichtigsten?
Lösung: Ob ihre Objekte überall dort erwartungsgemäß funktionieren, wo die Oberklasse verlangt wird. — Eine Unterklasse ist nur dann ein tragfähiger Subtyp, wenn sie nicht nur ähnlich aufgebaut, sondern auch verhaltensmäßig austauschbar ist.

Passend dazu

8 Kapitel

Kapitel 1