EMZETT.
Login

Inheritance

Kurz: Eine Klasse (Unterklasse) übernimmt Felder und Methoden einer anderen Klasse (Oberklasse) mit dem Schlüsselwort extends — spart Wiederholung und bildet “ist-ein”-Beziehungen ab.

Genauer: Java erlaubt nur Einfachvererbung: eine Klasse kann mit extends nur von genau einer Oberklasse erben (im Gegensatz zu Interfaces, von denen eine Klasse mehrere gleichzeitig implementieren kann). Die Unterklasse kann geerbte Methoden mit derselben Signatur neu implementieren (“Overriding”) — Grundlage für Polymorphism.

class Tier { void geraeusch() { System.out.println("..."); } }
class Hund extends Tier { void geraeusch() { System.out.println("Wuff"); } }

Im Detail

class Tier {
    protected String name;
 
    Tier(String name) { this.name = name; }
 
    void geraeusch() { System.out.println(name + " macht ein Geräusch"); }
    void fressen() { System.out.println(name + " frisst"); } // wird nicht überschrieben
}
 
class Hund extends Tier {
    Hund(String name) {
        super(name); // ruft den Konstruktor von Tier auf - Pflicht als erste Zeile
    }
 
    @Override
    void geraeusch() { System.out.println(name + " bellt: Wuff!"); }
}
 
Tier t = new Hund("Rex"); // Polymorphie: Variable vom Typ Tier, Objekt vom Typ Hund
t.geraeusch(); // "Rex bellt: Wuff!" - die ÜBERSCHRIEBENE Version läuft, nicht die von Tier
t.fressen();   // "Rex frisst" - geerbt, unverändert

Java erlaubt bewusst nur Einfachvererbung (extends nur EINE Oberklasse), um das sogenannte “Diamond Problem” zu vermeiden: würde eine Klasse von zwei Oberklassen erben, die beide dieselbe Methode unterschiedlich implementieren, wäre unklar, welche Version gilt. Interfaces umgehen dieses Problem, weil sie (traditionell) keine eigene Implementierung mitbringen — eine Klasse kann daher beliebig viele Interfaces implementieren, aber nur von einer Klasse erben. protected (statt private) bei Feldern der Oberklasse erlaubt Unterklassen direkten Zugriff, während der Zugriff von außerhalb der Vererbungshierarchie weiterhin verboten bleibt — ein Mittelweg zwischen Kapselung und praktischer Wiederverwendbarkeit in Unterklassen.

final verhindert weitere Vererbung

Eine Klasse (oder eine einzelne Methode) lässt sich mit final gezielt vor weiterer Vererbung bzw. Überschreibung schützen:

final class Konstante {} // kann von KEINER Klasse mehr erweitert werden
 
class Basis {
    final void kritischeMethode() {} // kann in Unterklassen NICHT überschrieben werden
}

Bekannte Beispiele aus der Standardbibliothek: String selbst ist final — genau deshalb kann niemand eine eigene MeinString extends String-Klasse bauen, was Sicherheits- und Konsistenzgarantien der Klasse schützt (z. B. dass ein String-Objekt wirklich immer unveränderlich bleibt).

Häufiger Fallstrick: Feld-Verdeckung statt Überschreibung

Anders als Methoden werden FELDER bei Vererbung nicht “überschrieben”, sondern nur verdeckt (“Shadowing”) — welches Feld tatsächlich verwendet wird, hängt vom deklarierten Typ der Referenz ab, nicht vom tatsächlichen Objekttyp:

class A { String name = "A"; }
class B extends A { String name = "B"; } // verdeckt A.name, überschreibt es NICHT
 
A a = new B();
System.out.println(a.name); // "A" - der deklarierte Typ (A) entscheidet bei Feldern!

Das unterscheidet sich fundamental von Methoden, bei denen der TATSÄCHLICHE Objekttyp (dynamische Bindung) entscheidet, welche Version läuft — ein häufiger Verwirrungspunkt für Einsteiger, die Felder und Methoden gedanklich gleich behandeln.

Siehe auch: Polymorphism, super-Keyword, Interface, OOP