Logo Wissenstransfer Gerhard at CichnaDotCom

>> Wissensdatenbank / Objektorientiertes Programmieren

Vererbung

Modellierung von Vererbung im Klassendiagramm

Attribute, die alle Klassen gemein haben, können in einer sogenannten Oberklasse zusammengefasst werden. Über die "ist ein/e"-Beziehung werden diese Attribute an die verbundenen Klassen weitergegeben und müssen dort dann nicht noch einmal aufgeführt werden.

In UML-Klassendiagrammen wird für die "ist ein/e"-Beziehung eine eigene Notation verwendet. Man zieht zwischen zwei Klassen eine Linie und zeichnet an das Ende eine geschlossene nicht ausgefüllte Pfeilspitze.

Vererbung Klassendiagramm

Die "ist ein/e"-Beziehung ist ein wichtiges Konzept in der Objektorientierung und wird als Vererbungsbeziehung bezeichnet. Man drückt mit dieser Beziehung aus, dass eine Klasse A wie eine andere Klasse B betrachtet werden kann. Die "ist ein/e"-Beziehung drückt aus, dass eine Klasse eine spezielle Art einer anderen Klasse ist.

Eine Klasse wird als Oberklasse bezeichnet, wenn andere Klassen von ihr ableiten. Diese werden "Unterklassen" genannt. Für den Begriff Oberklasse werden häufig auch die Begriffe "Basisklasse" oder "Superklasse" verwendet, für den Begriff Unterklasse die Begriffe "abgeleitete Klasse" oder "Subklasse". Man sagt auch, dass eine Unterklasse von einer Oberklasse ableitet.

Durch Vererbung werden alle Attribute der Oberklasse an die Unterklasse weitergegeben.

Die Unterklassen sind eine speziellere Art der Oberklasse. Sie besitzen alle Attribute der Oberklasse, definieren darüber hinaus aber noch weitere Attribute, die für die eigene Beschreibung wichtig sind.

Vererbung ermöglicht somit, über Dinge im Allgemeinen und im Speziellen zu sprechen, ohne sich dabei unnötig zu wiederholen.

Die Vererbungsbeziehung ist transitiv, sie vererbt sich gewissermaßen mit:
Vererbung ist transitiv

Durch Vererbung werden nicht nur die Attribute der Oberklasse an die Unterklassen weitergegeben. Es werden auch die Methoden und Assoziationen der Oberklasse vererbt. Auf diese Weise wird nicht nur die Gestalt der Oberklasse vererbt, sondern auch ihr Verhalten und ihre Verwendung.
Vererbung von Assoziation

Objekte der Unterklassen besitzen dieselben Methoden wie Objekte der Oberklassen und lassen sich deshalb auf dieselbe Art und Weise ansprechen. Über die vererbten Assoziationen wird deutlich, wo sich Objekte der Unterklassen einsetzen lassen und auf welche Weise sie an diesen Stellen verwendet werden.

Mithilfe der Vererbung werden objektorientierte Systeme strukturiert. Durch das Definieren von Oberklassen werden Gemeinsamkeiten in bereits definierten Klassen zusammengefasst. Änderungen an der Oberklasse werden an alle abgeleiteten Klassen weitergegeben. Durch Ableiten von einer bereits definierten Oberklasse wird eine neue Klasse für einen bestimmten Anwendungsfall spezialisiert. Auf diese Weise wird Redundanz vermieden und die Übersichtlichkeit des Systementwurfs im Modell verbessert.

Es ist im Allgemeinen üblich, dass eine Unterklasse von nur einer einzelnen Oberklasse ableitet. Bei mehreren Oberklassen würde eine Unterklasse die Vereinigungsmenge der Attribute und Methoden aller Oberklassen erben. Dabei kann es dazu kommen, dass in zwei unterschiedlichen Oberklassen dieselben Namen vergeben wurden, allerdings mit unterschiedlichen Bedeutungen. Dieser Konflikt führt allerdings häufig zu einer verringerten Verständlichkeit des Modells und zu Fehlern in der Modellierung. Darüber hinaus kann es sein, dass die Programmiersprache, in der das modellierte System umgesetzt wird, die Mehrfachvererbung nicht erlaubt. Java beispielsweise erlaubt sie nicht.

Programmieren von Vererbung in Java

In Java realisiert man die Vererbung über ein neues Schlüsselwort bei der Deklaration einer Klasse. Anders als die schon vorgestellten Begriffe "Ableitung" oder "Spezialisierung", nennt Java das Konzept Erweiterung. Eine Unterklasse "erweitert" eine Oberklasse, indem sie neue Attribute und Methoden definiert beziehungsweise modifiziert. Das Schlüsselwort lautet deswegen auch extends.

Die Vererbungsbeziehung in Java geht von der abgeleiteten Klasse aus, genau wie die Richtung des Assoziationspfeils in UML Klassendiagrammen. Um eine Vererbung in Java auszudrücken, gibt man in der Klassendeklaration der Unterklasse an, von welcher Oberklasse man ableitet.

Die Unterklasse erbt alle Attribute und Methoden der Oberklasse. Eine Ausnahme gilt für die Attribute und Methoden, die in der Oberklasse als private deklariert wurden. Diese sind auch in einer Unterklasse nicht verfügbar. Geerbte Attribute und Methoden behalten ansonsten aber ihre Sichtbarkeit: Als public deklarierte Attribute und Methoden bleiben also weiterhin public.
Vererbung mittels extends

Beispiel:

Oberklasse:
public class Artikel {
    protected String hersteller;
    protected String titel;
    protected String artikelnummer;

    public String getBeschreibung() {
        return artikelnummer + ":" + hersteller + ";" + titel;
    }
    ...
}


Unterklasse: (erbt von Oberklasse Artikel und definiert ein weiteres Attribut)
public class Buch extends Artikel {
    private Autor autor;

    public Autor getAutor() {
        return autor;
    }
    public void setAutor(Autor autor) {
        this.autor = autor;
    }
}

Nach dem Instanziieren eines Buch-Objektes können die geerbten Attribute und Methoden wie normale Instanzattribute und -methoden verwendet werden. Die geerbten Attribute erhalten ihre Werte aus der Oberklasse. Beim Aufrufen von geerbten Methoden werden die Implementierungen aus der Oberklasse verwendet.

Zugriff auf geerbte Methoden:
public static void main(String[] args) {
    Buch buch = new Buch();
    buch.setAutor("Adam Autor");
    buch.setTitel("Buch, Bücher, Büchner");
    buch.setHersteller("V Verlag");

    System.out.println(buch.getBeschreibung());
}

Es wird ein Objekt buch erzeugt und sowohl das neue Attribut als auch die geerbten Attribute über die geerbten Getter- und Setter-Methoden gesetzt. Das Objekt buch kann also wie ein Artikel-Objekt verwendet werden, da es dieselben Attribute und Methoden wie ein Artikel hat.

Es ist auch möglich, eine Variable vom Typ Artikel zu deklarieren und ihr ein Buch-Objekt zuzuweisen. Dies wird Zuweisungskompatibilität genannt: Jedes Buch ist ein Artikel und kann als solcher behandelt werden. Andersherum ist diese Kompatibilität nicht gegeben: Nicht jeder Artikel ist ein Buch. Die Klasse Buch deklariert ein neues Attribut, das nicht in Artikel vorhanden ist.
Zuweisungskompatibilität

Es muss bei der Zuweisungskompatibilität noch auf eine andere Sache geachtet werden: Der Typ der Variable entscheidet, welche Attribute und Methoden aufgerufen werden können. In der folgenden Abbildung wird eine Variable artikel vom Typ Artikel deklariert und ihr eine neue Instanz von Buch zugewiesen. Auf der Variable artikel können danach nur die Methoden der Klasse "Artikel" aufgerufen werden. Beim Aufruf von Methoden der Klasse Buch erzeugt der Compiler einen Fehler.
Zuweisungskompatibilität abhängig vom Typ der Variable

Die Zuweisungskompatibilität wird verwendet, um in großen Softwaresystemen eine möglichst lose Koppelung der einzelnen Klassen untereinander zu gewährleisten. Soll beispielsweise eine Liste der Hersteller über die gesamte Artikelmenge erstellt werden, ist es egal, ob es sich bei den einzelnen Artikeln um Bücher, Spiele oder Filme handelt. Die Lösung kann also unabhängig von den konkreten Ausprägungen implementiert werden, indem man sich auf die in der Klasse "Artikel" deklarierten Funktionalitäten konzentriert. So können im Verlauf der Implementierung noch weitere Unterklassen von Artikel erstellt werden - zum Beispiel für Spielzeuge -, ohne dass die Schleife, die die Herstellerliste zusammenstellt, dafür angepasst werden muss.
Generische Schleife über alle Artikel, die eine Liste der Hersteller erstellt

Unterklassen erben zwar die Attribute und Methoden der Oberklasse, sind aber nicht zwingend an die Implementierung gebunden. So gibt zum Beispiel die in der Klasse "Artikel" definierte Methode getBeschreibung für die Unterklassen eine unvollständige Beschreibung aus (z.B. fehlt bei Buch der Autor). Das kann aber nur in den Unterklassen realisiert werden. Die Oberklasse weiß nichts von den zusätzlichen Attributen der Unterklassen, die noch mit ausgegeben werden müssten. Die Unterklassen müssen also die Implementierung der geerbten Methode getBeschreibung für ihre eigenen Zwecke anpassen. Dieser Vorgang wird Überschreiben genannt.

Methoden können in Unterklassen überschrieben werde, indem man eine Methode mit derselben Signatur erstellt und einen neuen Methodenrumpf implementiert.
Überschreiben einer Methode

Erzeugt man eine Instanz der Unterklasse und greift auf geerbte Attribute oder Methoden zu, so werden die überschriebenen Varianten ausgewählt. Die Instanz bestimmt, welche Implementierung ausgeführt wird.

Beim Überschreiben von Attributen und Methoden wird die ursprüngliche Implementierung aus der Oberklasse von der neuen Implementierung überlagert. Es ist aber möglich, auf die bestehende Implementierung in der Oberklasse zuzugreifen. Diese Möglichkeit bietet das Java-Schlüsselwort super. Mithilfe von super wird die Oberklasse angesprochen und es kann auf deren Attribute und Methoden zugegriffen werden. Da jede Unterklasse nur eine Oberklasse haben kann, ist mit super genau definiert, welche Klasse gemeint ist. super geht eine Ebene in der Vererbungshierarchie nach oben und wählt die dortige Implementierung aus.
Zugriff auf Oberklasse mit super

Mittels super können Implementierungen aus der Oberklasse erweitert werden. Zuerst ruft man die bereits vorhandene Implementierung auf und führt dann zusätzliche Anweisungen aus, die für die Funktionalität in der Unterklasse wichtig sind.
Zugriff auf Implementierung in der Oberklasse mittels super