Vererbung I: Grundlagen 👪

Klassen können Eigenschaften (eng.: properties bzw. Felder) und Fähigkeiten (Methoden) von anderen Klassen erben.

Grundlagen

Was ist Vererbung?

Bei Vererbung handelt es sich um eines der wichtigsten und grundlegendsten Konzepte der 🔭 Objektorientierten Programmierung. Klassen können andere Klassen erweitern, wodurch sie die Eigenschaften und Fähigkeiten, die in der erweiterten Klasse definiert sind, von dieser erben (eng.: inherit).

Wozu braucht man Vererbung?

Eigenschaften und Fähigkeiten von Klassen werden bei einer Vererbung (Erweiterung durch andere Klassen) zu Eigenschaften und Fähigkeiten aller jeweils erbenden Klassen. So wird nicht nur Code wiederverwendet, sondern die Logik dieser Eigenschaften und Fähigkeiten lässt sich für alle erbenden Klassen an einer einzigen Stelle ändern, ohne den Code jeder einzelnen erbenden Klasse ändern zu müssen.

Programm-Code, der Vererbung nutzt, modelliert reale Phänomene und Beziehungen auf semantisch klare Weise. Am besten lässt sich das an einem Beispiel verdeutlichen:

👉 Ein Wohnhaus mit Küche, Diele, Bad und 3 Zimmern erweitert das Konzept eines Gebäudes (von Menschen gebaut, nicht mobil). Somit “erbt” das Wohnhaus die Eigenschaften des Konzeptes Gebäude, da es ebenfalls von Menschen gebaut und nicht mobil ist.

Vererbung ermöglicht außerdem 🔗Polymorphie unter Typen, denn jede Instanz einer Klasse ist auch eine Instanz aller Superklassen (siehe Terminologie!). Bezogen auf das Beispiel oben bedeutet das nichts anderes als “Ein Wohnhaus ist ein Gebäude”.

Terminologie

Die vererbenden Klassen werden als Superklassen, Elternklassen oder Überklassen (Englisch parent class oder super class) bezeichnet, die erbenden hingegen respektive als Subklassen, Kindklassen oder Unterklassen (Englisch child class oder sub class).
Die erbende Klasse erweitert die Klasse, von der sie erbt, da sie (normalerweise) Eigenschaften und Fähigkeiten besitzt, die über jene der Superklasse hinausgehen.

extends

Eine Klasse B erweitert eine Klasse A, indem ihre Signatur mit dem Schlüsselwort extends (eng.: erweitert) versehen wird:

public class B extends A {
  // ...
}

Klasse B erbt somit von Klasse A (und deren Superklassen, also mindestens auch von Object!)

💬 Wenn man aus irgendeinem Grund der Meinung ist, dass eine Klasse nicht erweitert werden sollte, dann kann man dies übrigens verhindern, indem man sie mit dem Schlüsselwort final zu einer “finalen” Klasse macht : public final class NoChildrenPlease { ... }

Die Klasse Object

Alle Klassen erben automatisch von der Klasse Object. Das ergibt Sinn: Denn die Instanzen aller Klassen sind immerhin Objekte. Eine Klasse B, die eine Klasse A erweitert (und also von dieser erbt) ist ebenfalls eine Instanz von Object - denn A erweitert implizit Object.

inheritance tree

🔗 Hier findest du die offizielle Dokumentation der Klasse Object

Die Klasse Object besitzt eigene Methoden, die selbstverständlich auch automatisch an alle Java-Klassen vererbt werden und somit Methoden aller Objekte in Java sind. Einige davon sollte man unbedingt von Anfang an kennen:

👩‍🏫 Alle diese Methoden der Klasse Object werden in anderen Klassen sehr häufig überschrieben, weil ihre optimale Implementation sehr davon abhängt, worum es sich bei der jeweiligen Klasse handelt. Mehr dazu findest du hier.

Keine Mehrfachvererbung

Jede Klasse kann in Java nur von einer anderen Klasse erben. Es ist keine Mehrfachvererbung möglich!

// jede Person ist auch ein Object
public class Person(){
  protected String name;
}

// jeder User ist auch eine Person und ein Object
// und hat einen Namen UND einen Username
public class User extends Person {
  private String userName;
}

💬 Der Zugriffsmodifizierer protected erlaubt nur den Zugriff durch erbende Klassen! Siehe auch hier.

super

Mit dem Schlüsselwort super referenziert eine Klasse ihre Superklasse (siehe Vererbung)!

Es ist üblich (und sinnvoll) in Konstruktoren zuerst den Konstruktor der jeweiligen Superklasse aufzurufen:

public class User extends Person {

  public User(){
    super(); // Konstruktor von "Person"
    // Konstruktor-Code von "User"...
  }

  // ...
}

Natürlich lassen sich auch Konstruktoren der Superklasse aufrufen, wenn diese Parameter entgegennehmen:

public class User extends Person {

  private String mail;

  public User(String name, String mail){
    super(name); // Konstruktor von "Person"
    this.mail = mail;
  }

  // ...
}

instanceof

instanceof ist ein Operator, der die Zugehörigkeit eines Objektes zu einem bestimmten Typ überprüft:

User user = new User("Tonky McWigglefritts");
System.out.println(user instanceof Person); // true
System.out.println(user instanceof Object); // true
System.out.println(user instanceof CharSequence); // false
System.out.println(user instanceof List); // false

Überschreiben von Methoden

Die Methoden von Klassen können in Subklassen (also in erweiternden/erbenden Klassen) überschrieben werden. Ein Aufruf referenziert dann diese überschreibende Methode der erweiternden Klasse und nicht mehr die Methode der Superklasse.

// jede Person ist auch ein Object
public class Person(){
  public String something(){
    // ...
  }
}

// jeder User ist auch eine Person und ein Object
// und hat einen Namen UND einen Username
public class User extends Person {
  @Override
  public String something(){
    // eigene, unabhängige Implementation!
  }
}

@Override

Eine überscheibende Methode wird im Quelltext per Konvention markiert durch die @Override-Annotation. Diese ist im Prinzip nur eine “Anmerkung”, die darauf hinweist, dass es sich um eine überschreibende Methode handelt. Sie ist aber durchaus wichtig, denn diese Anmerkung wird vom 👉Compiler überprüft:

📚 Aus den docs zu @Override:

Indicates that a method declaration is intended to override a method declaration in a supertype. If a method is annotated with this annotation type compilers are required to generate an error message unless at least one of the following conditions hold:

👉 @Override ist also nicht nötig, macht aber den Code lesbarer und veranlasst den Compiler dazu, hilfreiche Fehlermeldungen zu liefern. Es ist deshalb Konvention, diese Annotation zu verwenden!


🔗 Ein Artikel zu Vererbung in Java mit einem umfangreicheren Beispiel

🔗 w3schools zu Vererbung in Java