Vererbung

The Royal Baby

Prince George Alexander Louis of Cambridge (* 22. Juli 2013)

Vererbung – Prinz George

Biologische Systematik

Computersoftware

Exkurs: UML

Jede Klasse wird durch eine Box dargestellt, die aus drei Bereichen besteht:

  1. Name der Klasse in Fettdruck
  2. Attribute der Klasse mit Sichtbarkeit und Typ
  3. Methoden der Klasse mit Parametertypen und Rückgabetypen

Die Typen werden anders als in Java durch einen Doppelpunkt getrennt hinter das Attribut, die Methode und den Parameter geschrieben, also attribut : Typ bzw.
methodenName(parameterName : Typ) : Typ.

Sichtbarkeit wird später noch detailliert erläutert und ist hier nur der Vollständigkeit halber angegeben.

  • + → public
  • # → protected
  • ~ → default
  • - → private

Vererbung: Motivation

public class Mitarbeiter {
    public String name;
    public double gehalt;
    public Date geboren;

    public String getDetails() {
        return name + ", geb. am " + geboren +
                " hat ein Gehalt von " + gehalt;
    }
}
UML Darstellung der Klasse Mitarbeiter
UML Darstellung der Klasse Mitarbeiter
public class Manager {
    public String abteilung;
    public String name;
    public double gehalt;
    public Date geboren;

    public String getDetails() {
        return name + ", geb. am " + geboren +
                " hat ein Gehalt von " + gehalt;
    }
}
UML Darstellung der Klasse Manager
UML Darstellung der Klasse Manager

Vererbung

  • Vererbung (subclassing) wichtiges Merkmal objektorientierter Sprachen
  • Eigenschaften vorhandener Klassen werden auf neue Klassen übertragen
  • Man unterscheidet dabei zwischen
    • Einfachvererbung (single inheritance) - jede Klasse erbt von maximal einer anderen Klasse
    • Mehrfachvererbung (multiple inheritance) - eine Klasse kann von mehr als einer Klasse erben
  • Java erlaubt grundsätzlich nur Einfachvererbung

Einfachvererbung

Mehrfachvererbung

Man hat sich in Java bewusst dazu entschlossen auf Mehrfachvererbung zu verzichten, da sie zu erheblichen Problemen führen kann. Die Sprache C++, die Mehrfachvererbung unterstützt, ist berühmt/berüchtigt für die Probleme, die entstehen, wenn eine Klasse gleichnamige Attribute und Methoden von mehreren Eltern erbt.

Da es allerdings ein sehr häufiger Anwendungsfall von Mehrfachvererbung in der Beschreibung von Schnittstellen (Interfaces) liegt, hat man für diesen Fall in Java ein gesondertes Konstrukt ersonnen, das später behandelt wird. So ist es gelungen, dass man ohne Mehrfachvererbung sehr gut objektorientiert mit Java programmieren kann.

Neben der Fähigkeit Daten und Methoden zu Klassen bzw. Objekten zusammenzufassen, stellt Vererbung ein weiteres wichtiges Merkmal objektorientierter Sprachen dar. Fehlt die Vererbung spricht man von objektbasierten Sprachen (z. B. ist Visual Basic 6 nur objektbasiert).

Man spricht auch häufig von Ableiten oder Ableitung, wenn man Vererbung meint. Man leitet dann die Klasse B von der Klasse A ab.

  • Eine Klasse wird von einer bestehenden Klasse durch Angabe des Schlüsselwortes extends abgeleitet
    public class B extends A {}
  • Die abgeleitete Klasse erbt alle Attribute und Methoden der Oberklasse
  • Man nennt die Klasse von der abgeleitet wird auch
    • Superklasse oder Oberklasse (superclass)
    • Elternklasse (parent class)
  • Die Klasse, die ableitet nennt man auch
    • Subklasse oder Unterklasse (subclass)
    • Kindklasse (child class)
    • Abgeleitete Klasse

Die Aussage, dass die Subklasse alle Attribute und Methoden erbt, wird später noch einmal genauer beleuchtet.

Die Vererbungsbeziehung ist transitiv, d. h. sie bezieht sich nicht nur auf eine direkte Oberklasse, sondern geht über mehrere Hierarchieebenen hinweg. Mit jeder weiteren Ableitung erbt die Subklasse alle bereits an ihre Oberklasse vererbten Attribute und Methoden.

Wenn die Anzahl der Vererbungsebenen zu groß wird, kann die Verständlichkeit des Programms deutlich leiden. Daher sollte man vorsichtig mit Vererbungsbeziehungen umgehen, die 6 oder mehr Ebenen haben.

Vererbung: Beispiel

public class Mitarbeiter {
    public String name;
    public double gehalt;
    public Date geboren;

    public String getDetails() {
        return name + ", geb. am " + geboren +
                " hat ein Gehalt von " + gehalt;
    }
}

public class Manager extends Mitarbeiter {
    public String abteilung;
}

public class Geschaeftsfuehrer extends Manager {
    public boolean prokura = true;
}

Abgrenzung: Vererbung / Aggregation

  • Vererbung modelliert eine „ist-eine“ Beziehung zwischen Klassen: ein Manager ist ein Mitarbeiter
  • Aggregationen modellieren eine „hat“ Beziehung zwischen Klassen: ein Manager hat Kinder
  • Kompositionen sind Aggregationen, bei denen die Elemente nicht getrennt existieren können: ein Manager hat ein Gehirn

Vererbung ist nicht immer das richtige Mittel. Man sollte sie nur einsetzen, wenn man eine „ist-eine“ Beziehung zwischen den Klassen hat, d. h. wenn man sagen kann B ist ein A, z. B. ein Manager ist ein Mitarbeiter, eine Ente ist ein Tier, ein Auto ist ein Fahrzeug etc. Hat man keine „ist-eine“, sondern eine „hat-eine“ Beziehung, so liegt eine Aggregation vor, bei der man nicht mit Vererbung arbeiten sollte. Beispiele für eine Aggregation sind: ein Manager hat Kinder etc.

Man spricht von einer Komposition, wenn die Teile der Aggregation dieselbe Lebensdauer wie das Gesamte haben. So ist z. B. ein Manager nicht ohne Gehirn vorstellbar, sodass Manager und Gehirn eine Komposition bilden.

Wenn man das Konzept der Vererbung zum ersten Mal entdeckt, ist man im Allgemeinen versucht mehr Vererbungsbeziehungen aufzubauen als angemessen wäre. Daher sollte man sich vor jeder Ableitung fragen, ob es sich hier wirklich um eine Vererbungsbeziehung handelt oder eher um etwas anderes. Besonders berücksichtigen sollte man hierbei die Tatsache, dass Vererbung immer ein Verstoß gegen das Prinzip des Information Hiding darstellt, da ja die Subklasse auf eigentlich zu versteckende Eigenschaften Superklasse zugreift.

Zugriffskontrolle (access control)

  • Nicht alle Eigenschaften einer Klasse sollen für jeden Verwender sichtbar sein
  • Vier Zugriffsstufen für Variablen und Methoden
    • private - Nur Methoden innerhalb derselben Klasse dürfen zugreifen
    • default - Methoden innerhalb desselben Pakets dürfen zugreifen
    • protected - Methoden innerhalb desselben Pakets und Subklassen ausserhalb des Pakets dürfen zugreifen
    • public - Jeder darf zugreifen
  • Klassen und Interfaces können default und public sein
  • Gutes Design ⇒ minimale Zugriffsrechte für die Verwender

Die hier beschriebene Zugriffskontrolle dient dazu, die bereits beschriebenen Prinzipien der Kapselung und des Information-Hiding bei Java-Klassen umzusetzen.

Die Zugriffsstufe default wird häufig als package-friendly oder nur package bezeichnet. Für default gibt es kein Schlüsselwort, sondern diese Zugriffsstufe wird durch das Fehlen eines Schlüsselworts angezeigt.

Die höheren Stufen umfassen immer alle niedrigeren Stufen, d. h. eine mit default gekennzeichnete Methode darf natürlich von innerhalb derselben Klasse aufgerufen werden.

Der Zugriff, der über protected gewährt wird, bezieht sich auf die geerbten Attribute und Methoden, erlaubt aber keinen generellen Zugriff auf die Felder der Superklasse aus der Subklasse.

package pr2.pack1;
public class A {
    protected int i;
    protected void method1() { }
}
package pr2.pack2;
import pr2.pack1.A;

public class B extends A {
    protected void method2() {
        method1();
        i++;
    }
    public void doIt(A a) {
        a.i++; // FEHLER!!!
    }
}

protected gewährt einer Subklasse Zugriff auf ererbte Eigenschaften und Methoden einer Superklasse, nicht jedoch auf Interna, die nichts mit der Vererbung zu tun haben. Daher kann im Beispiel die Methode doIt() nicht auf das Feld a zugreifen, da es sich hier nicht um das geerbte Feld in B handelt, sondern um ein Feld in einer übergebenen Instanz von A.


Copyright © 2025 Thomas Smits