/
/
·
·
/
/
·
·
  • Einführung
  • Die Idee in einem Satz
  • Das Problem: Vererbung für Code-Reuse
  • Das Fragile-Base-Class-Problem
  • Delegation, Flexibilität und die [Klassen](/themen/klassen-objekte)-Explosion
  • Wann Vererbung trotzdem richtig ist
  • Klausur-Faustregeln
  • Häufige Stolpersteine
ThemenProgrammiergrundlagenKomposition vs. Vererbung
Programmiergrundlagen·4Lerneinheiten·30min·Stand18.07.2026

Komposition vs. Vererbung.

Du kannst Code auf zwei Arten wiederverwenden: erben (extends) oder ein Objekt als Feld halten und an es delegieren. Anfänger greifen reflexartig zur Vererbung, weil sie zuerst gelehrt wird. Fortgeschrittene Programmierung fragt aber: war das die richtige Wahl? Dieses Topic zeigt, wann Vererbung trägt (echte ist ein-Beziehung) und wann sie zur Falle wird, und warum die Standard-Empfehlung lautet: im Zweifel Komposition (Joshua Bloch, Effective Java, Item 18).

Was du können musst:

  • IS-A vs. HAS-A sicher unterscheiden und einer Modellierung zuordnen
  • Komposition als Feld + Delegation implementieren
  • das Fragile-Base-Class-Problem erklären (warum Vererbung Kapselung bricht)
  • die kombinatorische Explosion von Vererbungs-Hierarchien erkennen
  • begründen, wann Vererbung trotzdem richtig ist (Liskov, designed for extension)

Vererbung modelliert "ein X ist ein Y" und übernimmt die Implementierung der Oberklasse; Komposition modelliert "ein X hat ein Y", hält das Y als Feld und leitet Aufrufe daran weiter (Delegation).

Du brauchst einen Stack (Stapel: nur push und pop). ArrayList kann doch schon alles, also erbst du einfach:

// ANTI-PATTERN: Vererbung nur um Code zu sparen
class Stack<E> extends ArrayList<E> {
    void push(E e) { add(e); }
    E pop() { return remove(size() - 1); }
}

Sieht praktisch aus, ist aber kaputt: dein Stack hat jetzt auch add(int index, E e), remove(int index), set(...), clear(), geerbt von ArrayList. Jemand kann stack.add(0, x) aufrufen und mitten in den Stapel schreiben. Die Invariante "Zugriff nur oben" ist hin. Du hast die ganze Schnittstelle der Oberklasse mitgeerbt, auch die Teile, die du gar nicht willst.

// HAS-A: Stack HÄLT eine Liste und zeigt nur push/pop nach außen
class Stack<E> {
    private final List<E> elems = new ArrayList<>();   // Komposition

    void push(E e) { elems.add(e); }
    E pop() {
        if (elems.isEmpty()) throw new EmptyStackException();
        return elems.remove(elems.size() - 1);          // Delegation
    }
    boolean isEmpty() { return elems.isEmpty(); }
}

Mit Komposition entscheidest du, welche Methoden nach außen sichtbar sind. Die Liste ist private, niemand kann an der Invariante vorbei.

Lade Visualisierung...

Vererbung bricht Kapselung: die Subklasse hängt an Implementierungsdetails der Oberklasse, die sich ändern können. Der Klassiker (aus Bloch, Effective Java): ein HashSet, das mitzählt, wie viele Elemente je hinzugefügt wurden.

class ZaehlendesSet<E> extends HashSet<E> {
    private int hinzugefuegt = 0;

    @Override public boolean add(E e) {
        hinzugefuegt++;
        return super.add(e);
    }
    @Override public boolean addAll(Collection<? extends E> c) {
        hinzugefuegt += c.size();
        return super.addAll(c);
    }
    int getHinzugefuegt() { return hinzugefuegt; }
}

var s = new ZaehlendesSet<String>();
s.addAll(List.of("a", "b", "c"));
System.out.println(s.getHinzugefuegt());   // erwartet 3 ... liefert 6!

Warum 6? Weil HashSet.addAll intern selbst add aufruft (self-use). Also zählt addAll die 3 Elemente, und das von ihm aufgerufene überschriebene add zählt sie nochmal. Du müsstest wissen, dass addAll sich auf add abstützt, ein undokumentiertes Implementierungsdetail, das in der nächsten Java-Version anders sein kann.

Dieses konkrete Beispiel steht für ein allgemeines Muster, die fragile Basisklasse: deine Subklasse kann brechen, ohne dass du sie anfasst, nur weil sich die Oberklasse intern ändert. Komposition hat dieses Problem nicht: das gehaltene Objekt arbeitet über seine eigene öffentliche Schnittstelle und ruft keine überschriebenen Methoden deiner Klasse auf, es entsteht keine implizite self-use-Kopplung (außer du gibst ihm bewusst einen Callback).

Lade Visualisierung...

Delegation heißt: deine Methode macht die Arbeit nicht selbst, sondern reicht sie an das gehaltene Objekt weiter. Das entkoppelt: du programmierst gegen ein Interface, nicht gegen eine konkrete Oberklasse, und kannst das gehaltene Objekt zur Laufzeit austauschen (das ist im Kern das Strategy-Pattern). Strategy heißt hier: das austauschbare Verhalten steckt in einem eigenen Objekt, nicht fest verdrahtet in der Auto-Klasse.

interface Antrieb { void starten(); }

class Elektro implements Antrieb {
    public void starten() { System.out.println("leise surren"); }
}
class Benzin implements Antrieb {
    public void starten() { System.out.println("brummen"); }
}

class Auto {
    private Antrieb antrieb;                     // HAS-A
    Auto(Antrieb antrieb) { this.antrieb = antrieb; }
    void setAntrieb(Antrieb a) { this.antrieb = a; }   // zur Laufzeit wechselbar!
    void losfahren() { antrieb.starten(); }      // Delegation
}

var auto = new Auto(new Benzin());
auto.losfahren();              // brummen
auto.setAntrieb(new Elektro());
auto.losfahren();              // leise surren

Mit Vererbung müsstest du jede Kombination als eigene Klasse bauen: BenzinAuto, ElektroAuto, BenzinLKW, ElektroLKW. Bei F Fahrzeug-Typen und A Antrieben sind das F × A Klassen (multiplikativ). Mit Komposition: F + A + 1 Klassen (additiv). Genau das kannst du im interaktiven Tab durchspielen.

Vererbung ist nicht verboten, sie ist nur kein Default. Sie passt, wenn alle Punkte gelten:

  1. Es ist eine echte IS-A-Beziehung ("ein Hund ist ein Tier").
  2. Das Liskov-Substitutionsprinzip hält: überall, wo die Oberklasse erwartet wird, darf die Subklasse stehen, ohne dass etwas bricht.
  3. Die Oberklasse ist für Vererbung entworfen und dokumentiert (oder du kontrollierst sie, gleiches Paket). Bloch: "Inheritance is appropriate only ... within a package, or when ... designed and documented for extension."

1. Sprachtest IS-A vs. HAS-A. "Ein Auto ist ein Motor"? Nein, ein Auto hat einen Motor, also Komposition. Sag den Satz laut, dann weißt du es.

2. Im Zweifel Komposition (Bloch, Item 18). Sie ist lose gekoppelt, flexibler und bricht keine Kapselung. Vererbung ist die Ausnahme, die du begründen musst.

3. Komposition = Feld + Delegation. Halte das andere Objekt private als Feld und leite nur die Methoden weiter, die du wirklich anbieten willst. So bleibt die Schnittstelle minimal.

4. Vererbung nur bei echtem IS-A plus Liskov. Die Subklasse muss überall für die Oberklasse einsetzbar sein. Kannst du das nicht garantieren, ist Vererbung falsch.

5. Verhalten zur Laufzeit wechselbar nötig? Komposition. Bei Vererbung steht die Klasse eines Objekts mit new fest (ein ElektroAuto bleibt eines) und lässt sich nicht mehr austauschen; ein per Komposition gehaltenes Objekt (Strategy) wechselst du dagegen jederzeit per Setter.

6. Vererbung nur bei kontrollierter/dokumentierter Basisklasse. Eine fremde Klasse zu erweitern (z.B. ArrayList) ist riskant, weil du an ihre internen Aufrufe (self-use) gebunden bist.

1. Vererbung nur zum Code-Sparen. Stack extends ArrayList erbt add(index), remove(index) usw. und zerstört die Stapel-Invariante. Reuse rechtfertigt keine Vererbung, die Beziehung muss IS-A sein.

2. Fragile Basisklasse durch self-use. addAll ruft intern add; überschreibst du beide, zählst du doppelt. Subklassen hängen an undokumentierten Implementierungsdetails der Oberklasse.

3. Oberflächliches IS-A bricht Liskov. "Quadrat ist ein Rechteck" klingt richtig, aber setBreite/setHoehe verhalten sich beim Quadrat anders, Code, der mit Rechtecken rechnet, bricht. Sprache reicht nicht, das Verhalten muss passen.

4. Tiefe Vererbungstürme. Fünf Ebenen extends sind schwer nachzuvollziehen; eine Änderung oben kann unten alles brechen. Komposition bleibt flach und lokal.

5. Komposition mit Aggregation verwechseln. In UML ist ◆ (Komposition) eine starke Ganzes-Teil-Beziehung mit gemeinsamer Lebensdauer (eine Rechnung besitzt ihre Posten, die mit ihr verschwinden), ◇ (Aggregation) eine schwache (ein Team hat Spieler, die auch ohne das Team existieren). In Java gibt es dafür keine syntaktische Unterscheidung: beide sind ein Feld, der Unterschied liegt allein in Intention und Lebenszyklus.

6. "Java kann keine Mehrfachvererbung" als Sackgasse sehen. Du brauchst sie nicht: beliebig viele HAS-A-Felder plus mehrere Interfaces ersetzen Mehrfachvererbung sauber und ohne Diamant-Problem.

Dasselbe Problem, zwei Designs: links Vererbung (eine Klasse pro Fahrzeug-Antrieb-Kombination), rechts Komposition (Fahrzeug hält einen Antrieb über ein Interface).

Probier folgendes:

  • Starte bei 2 Fahrzeugen × 2 Antrieben. Beide Seiten sind noch klein.
  • Klick + Antriebsart (z.B. Hybrid): die Vererbungs-Seite bekommt pro Fahrzeug eine neue Klasse, die Komposition-Seite genau eine.
  • Füge noch ein paar Typen hinzu und beobachte die Formel: Vererbung F × A (multiplikativ) gegen Komposition F + A + 1 (additiv).

Das ist der praktische Grund für "favor composition over inheritance": neue Anforderungen kosten bei Komposition eine Klasse, bei Vererbung eine ganze Reihe.

Lade Visualisierung...

Faustregel zum Mitnehmen: Wenn dein Problem zwei unabhängige Achsen hat (hier: Fahrzeug-Art und Antrieb), modelliere die eine als Klasse und die andere als gehaltenes Objekt, nicht beide über Vererbung. Sonst multiplizieren sich die Achsen zu einer Klassen-Explosion. Komposition hält jede Achse separat und kombiniert sie zur Laufzeit, das ist genau die Idee hinter dem Strategy-Pattern.

Anmelden, um den Fortschritt zu speichern.

Nächster Schritt

Wenn du fertig bist: jetzt üben.

Aktives Abrufen festigt Wissen schneller als nochmal lesen.

War das hilfreich?

Verwandte Themen

  • Variablen und Datentypen
  • Bedingungen
  • Schleifen
  • Funktionen
  • Rekursion

Tools

Bald: Karteikarten · Spaced-Repetition · Mind-Map-Export

Fachliche Qualität
S-Tier · GoldstandardZuletzt geprüft am 01.06.2026

Diese Lerneinheit wurde für typische Bachelor-Klausuren konzipiert. So prüfen wir · Fehler entdeckt? Melde ihn uns oder markiere die fragliche Stelle direkt im Text oben.

Klausur-ÜbersichtKomplette Übersicht: alle Tabs als linearer Text zum Lernen
▾

Alle Tabs der Lerneinheit (Erklärung · Interaktiv verstehen · Praxis-Übung · Klausur-Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).

Inhalt dieser Übersicht

  1. Erklärung(Erklärung)
  2. Interaktiv verstehen(Visualisierung / Interaktiv)
  3. Praxis-Übung(Quiz / Klausurfragen)
  4. Klausur-Quiz(Quiz / Klausurfragen)
Teil 1·Erklärung

Erklärung

Du kannst Code auf zwei Arten wiederverwenden: erben (extends) oder ein Objekt als Feld halten und an es delegieren. Anfänger greifen reflexartig zur Vererbung, weil sie zuerst gelehrt wird. Fortgeschrittene Programmierung fragt aber: war das die richtige Wahl? Dieses Topic zeigt, wann Vererbung trägt (echte ist ein-Beziehung) und wann sie zur Falle wird, und warum die Standard-Empfehlung lautet: im Zweifel Komposition (Joshua Bloch, Effective Java, Item 18).

Was du können musst:

  • IS-A vs. HAS-A sicher unterscheiden und einer Modellierung zuordnen
  • Komposition als Feld + Delegation implementieren
  • das Fragile-Base-Class-Problem erklären (warum Vererbung Kapselung bricht)
  • die kombinatorische Explosion von Vererbungs-Hierarchien erkennen
  • begründen, wann Vererbung trotzdem richtig ist (Liskov, designed for extension)

Die Idee in einem Satz

Vererbung modelliert "ein X ist ein Y" und übernimmt die Implementierung der Oberklasse; Komposition modelliert "ein X hat ein Y", hält das Y als Feld und leitet Aufrufe daran weiter (Delegation).

Das Problem: Vererbung für Code-Reuse

Du brauchst einen Stack (Stapel: nur push und pop). ArrayList kann doch schon alles, also erbst du einfach:

// ANTI-PATTERN: Vererbung nur um Code zu sparen
class Stack<E> extends ArrayList<E> {
    void push(E e) { add(e); }
    E pop() { return remove(size() - 1); }
}

Sieht praktisch aus, ist aber kaputt: dein Stack hat jetzt auch add(int index, E e), remove(int index), set(...), clear(), geerbt von ArrayList. Jemand kann stack.add(0, x) aufrufen und mitten in den Stapel schreiben. Die Invariante "Zugriff nur oben" ist hin. Du hast die ganze Schnittstelle der Oberklasse mitgeerbt, auch die Teile, die du gar nicht willst.

// HAS-A: Stack HÄLT eine Liste und zeigt nur push/pop nach außen
class Stack<E> {
    private final List<E> elems = new ArrayList<>();   // Komposition

    void push(E e) { elems.add(e); }
    E pop() {
        if (elems.isEmpty()) throw new EmptyStackException();
        return elems.remove(elems.size() - 1);          // Delegation
    }
    boolean isEmpty() { return elems.isEmpty(); }
}

Mit Komposition entscheidest du, welche Methoden nach außen sichtbar sind. Die Liste ist private, niemand kann an der Invariante vorbei.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

Das Fragile-Base-Class-Problem

Vererbung bricht Kapselung: die Subklasse hängt an Implementierungsdetails der Oberklasse, die sich ändern können. Der Klassiker (aus Bloch, Effective Java): ein HashSet, das mitzählt, wie viele Elemente je hinzugefügt wurden.

class ZaehlendesSet<E> extends HashSet<E> {
    private int hinzugefuegt = 0;

    @Override public boolean add(E e) {
        hinzugefuegt++;
        return super.add(e);
    }
    @Override public boolean addAll(Collection<? extends E> c) {
        hinzugefuegt += c.size();
        return super.addAll(c);
    }
    int getHinzugefuegt() { return hinzugefuegt; }
}

var s = new ZaehlendesSet<String>();
s.addAll(List.of("a", "b", "c"));
System.out.println(s.getHinzugefuegt());   // erwartet 3 ... liefert 6!

Warum 6? Weil HashSet.addAll intern selbst add aufruft (self-use). Also zählt addAll die 3 Elemente, und das von ihm aufgerufene überschriebene add zählt sie nochmal. Du müsstest wissen, dass addAll sich auf add abstützt, ein undokumentiertes Implementierungsdetail, das in der nächsten Java-Version anders sein kann.

Dieses konkrete Beispiel steht für ein allgemeines Muster, die fragile Basisklasse: deine Subklasse kann brechen, ohne dass du sie anfasst, nur weil sich die Oberklasse intern ändert. Komposition hat dieses Problem nicht: das gehaltene Objekt arbeitet über seine eigene öffentliche Schnittstelle und ruft keine überschriebenen Methoden deiner Klasse auf, es entsteht keine implizite self-use-Kopplung (außer du gibst ihm bewusst einen Callback).

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

Delegation, Flexibilität und die Klassen-Explosion

Delegation heißt: deine Methode macht die Arbeit nicht selbst, sondern reicht sie an das gehaltene Objekt weiter. Das entkoppelt: du programmierst gegen ein Interface, nicht gegen eine konkrete Oberklasse, und kannst das gehaltene Objekt zur Laufzeit austauschen (das ist im Kern das Strategy-Pattern). Strategy heißt hier: das austauschbare Verhalten steckt in einem eigenen Objekt, nicht fest verdrahtet in der Auto-Klasse.

interface Antrieb { void starten(); }

class Elektro implements Antrieb {
    public void starten() { System.out.println("leise surren"); }
}
class Benzin implements Antrieb {
    public void starten() { System.out.println("brummen"); }
}

class Auto {
    private Antrieb antrieb;                     // HAS-A
    Auto(Antrieb antrieb) { this.antrieb = antrieb; }
    void setAntrieb(Antrieb a) { this.antrieb = a; }   // zur Laufzeit wechselbar!
    void losfahren() { antrieb.starten(); }      // Delegation
}

var auto = new Auto(new Benzin());
auto.losfahren();              // brummen
auto.setAntrieb(new Elektro());
auto.losfahren();              // leise surren

Mit Vererbung müsstest du jede Kombination als eigene Klasse bauen: BenzinAuto, ElektroAuto, BenzinLKW, ElektroLKW. Bei F Fahrzeug-Typen und A Antrieben sind das F × A Klassen (multiplikativ). Mit Komposition: F + A + 1 Klassen (additiv). Genau das kannst du im interaktiven Tab durchspielen.

Wann Vererbung trotzdem richtig ist

Vererbung ist nicht verboten, sie ist nur kein Default. Sie passt, wenn alle Punkte gelten:

  1. Es ist eine echte IS-A-Beziehung ("ein Hund ist ein Tier").
  2. Das Liskov-Substitutionsprinzip hält: überall, wo die Oberklasse erwartet wird, darf die Subklasse stehen, ohne dass etwas bricht.
  3. Die Oberklasse ist für Vererbung entworfen und dokumentiert (oder du kontrollierst sie, gleiches Paket). Bloch: "Inheritance is appropriate only ... within a package, or when ... designed and documented for extension."

Klausur-Faustregeln

1. Sprachtest IS-A vs. HAS-A. "Ein Auto ist ein Motor"? Nein, ein Auto hat einen Motor, also Komposition. Sag den Satz laut, dann weißt du es.

2. Im Zweifel Komposition (Bloch, Item 18). Sie ist lose gekoppelt, flexibler und bricht keine Kapselung. Vererbung ist die Ausnahme, die du begründen musst.

3. Komposition = Feld + Delegation. Halte das andere Objekt private als Feld und leite nur die Methoden weiter, die du wirklich anbieten willst. So bleibt die Schnittstelle minimal.

4. Vererbung nur bei echtem IS-A plus Liskov. Die Subklasse muss überall für die Oberklasse einsetzbar sein. Kannst du das nicht garantieren, ist Vererbung falsch.

5. Verhalten zur Laufzeit wechselbar nötig? Komposition. Bei Vererbung steht die Klasse eines Objekts mit new fest (ein ElektroAuto bleibt eines) und lässt sich nicht mehr austauschen; ein per Komposition gehaltenes Objekt (Strategy) wechselst du dagegen jederzeit per Setter.

6. Vererbung nur bei kontrollierter/dokumentierter Basisklasse. Eine fremde Klasse zu erweitern (z.B. ArrayList) ist riskant, weil du an ihre internen Aufrufe (self-use) gebunden bist.

Häufige Stolpersteine

1. Vererbung nur zum Code-Sparen. Stack extends ArrayList erbt add(index), remove(index) usw. und zerstört die Stapel-Invariante. Reuse rechtfertigt keine Vererbung, die Beziehung muss IS-A sein.

2. Fragile Basisklasse durch self-use. addAll ruft intern add; überschreibst du beide, zählst du doppelt. Subklassen hängen an undokumentierten Implementierungsdetails der Oberklasse.

3. Oberflächliches IS-A bricht Liskov. "Quadrat ist ein Rechteck" klingt richtig, aber setBreite/setHoehe verhalten sich beim Quadrat anders, Code, der mit Rechtecken rechnet, bricht. Sprache reicht nicht, das Verhalten muss passen.

4. Tiefe Vererbungstürme. Fünf Ebenen extends sind schwer nachzuvollziehen; eine Änderung oben kann unten alles brechen. Komposition bleibt flach und lokal.

5. Komposition mit Aggregation verwechseln. In UML ist ◆ (Komposition) eine starke Ganzes-Teil-Beziehung mit gemeinsamer Lebensdauer (eine Rechnung besitzt ihre Posten, die mit ihr verschwinden), ◇ (Aggregation) eine schwache (ein Team hat Spieler, die auch ohne das Team existieren). In Java gibt es dafür keine syntaktische Unterscheidung: beide sind ein Feld, der Unterschied liegt allein in Intention und Lebenszyklus.

6. "Java kann keine Mehrfachvererbung" als Sackgasse sehen. Du brauchst sie nicht: beliebig viele HAS-A-Felder plus mehrere Interfaces ersetzen Mehrfachvererbung sauber und ohne Diamant-Problem.

Teil 2·Visualisierung / Interaktiv

Interaktiv verstehen

Klassen-Explosion live

Dasselbe Problem, zwei Designs: links Vererbung (eine Klasse pro Fahrzeug-Antrieb-Kombination), rechts Komposition (Fahrzeug hält einen Antrieb über ein Interface).

Probier folgendes:

  • Starte bei 2 Fahrzeugen × 2 Antrieben. Beide Seiten sind noch klein.
  • Klick + Antriebsart (z.B. Hybrid): die Vererbungs-Seite bekommt pro Fahrzeug eine neue Klasse, die Komposition-Seite genau eine.
  • Füge noch ein paar Typen hinzu und beobachte die Formel: Vererbung F × A (multiplikativ) gegen Komposition F + A + 1 (additiv).

Das ist der praktische Grund für "favor composition over inheritance": neue Anforderungen kosten bei Komposition eine Klasse, bei Vererbung eine ganze Reihe.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

Faustregel zum Mitnehmen: Wenn dein Problem zwei unabhängige Achsen hat (hier: Fahrzeug-Art und Antrieb), modelliere die eine als Klasse und die andere als gehaltenes Objekt, nicht beide über Vererbung. Sonst multiplizieren sich die Achsen zu einer Klassen-Explosion. Komposition hält jede Achse separat und kombiniert sie zur Laufzeit, das ist genau die Idee hinter dem Strategy-Pattern.

Teil 3·Quiz / Klausurfragen

Praxis-Übung

Klausurfragen mit Lösungen (6)

F1.Welche Beziehung sollte als Komposition (HAS-A), nicht als Vererbung modelliert werden?

Antwort: Auto und Motor

Erklärung: Ein Auto HAT einen Motor (HAS-A), es IST kein Motor. Das gehört als Feld (Komposition) modelliert. Die anderen drei sind echte IS-A-Beziehungen (ein Hund ist ein Tier, ein Sparkonto ist ein Konto, ein Student ist eine Person) und passen zu Vererbung.

F2.Einen Stack als class Stack<E> extends ArrayList<E> zu bauen, ist gutes Design, weil man so Code spart.

Antwort: Falsch

Erklärung: Falsch. Der Stack erbt dann auch add(index), remove(index), set(...) usw. und jeder kann mitten in den Stapel schreiben, die Invariante 'Zugriff nur oben' ist kaputt. Besser: der Stack HÄLT eine private Liste (Komposition) und zeigt nur push/pop nach außen. Reuse allein rechtfertigt keine Vererbung.

Typ: Wahr/Falsch

F3.Ordne jede Modellierung der passenden Technik zu.

Zuordnungen:

  • Rechnung hat mehrere Positionen → Komposition (HAS-A)
  • ManagerIn ist eine Angestellte → Vererbung (IS-A)
  • Auto hält einen austauschbaren Antrieb → Komposition (HAS-A)
  • Kreis ist eine Form mit flaeche() → Vererbung (IS-A)

Erklärung: HAS-A (ein Ganzes hält Teile, oder hält ein austauschbares Verhalten) wird zu Komposition. IS-A (ein Spezialfall eines allgemeineren Typs) wird zu Vererbung. Der Sprachtest 'ist ein' vs. 'hat ein' trennt die Fälle zuverlässig.

Typ: Zuordnung

F4.Was ist der entscheidende Vorteil, den Antrieb eines Autos per Komposition (Feld + Interface) statt per Vererbung zu modellieren?
class Auto {
    private Antrieb antrieb;
    void setAntrieb(Antrieb a) { this.antrieb = a; }
    void losfahren() { antrieb.starten(); }
}

Antwort: Der Antrieb kann zur Laufzeit ausgetauscht werden

Erklärung: Per Komposition hält das Auto ein Antrieb-Objekt als Feld und kann es per Setter zur Laufzeit wechseln (Benzin -> Elektro). Vererbung legt das Verhalten zur Compile-Zeit fest (ein ElektroAuto bleibt ein ElektroAuto). Das ist im Kern das Strategy-Pattern.

F5.Du modellierst Fahrzeuge (Auto, LKW, Bus) und Antriebe (Benzin, Elektro, Hybrid). Wie viele Klassen brauchst du bei reiner Vererbung (eine Klasse pro Kombination) gegenüber Komposition (Fahrzeug hält Antrieb)?

Antwort: Vererbung 9, Komposition 7

Erklärung: Vererbung: 3 Fahrzeuge × 3 Antriebe = 9 Kombinationsklassen (BenzinAuto, ElektroAuto, ...). Komposition: 3 Fahrzeug-Klassen + 3 Antriebs-Klassen + 1 Antrieb-Interface = 7. Multiplikativ (F×A) gegen additiv (F+A+1): mit jeder neuen Achse rennt die Vererbung davon.

F6.Warum liefert das überschriebene Zähl-Set unten 6 statt 3, wenn man addAll mit 3 Elementen aufruft?
class ZaehlendesSet<E> extends HashSet<E> {
    int n = 0;
    public boolean add(E e) { n++; return super.add(e); }
    public boolean addAll(Collection<? extends E> c) {
        n += c.size();
        return super.addAll(c);
    }
}

Antwort: HashSet.addAll ruft intern add auf, daher wird doppelt gezählt

Erklärung: self-use: HashSet.addAll ruft intern für jedes Element das (überschriebene) add auf. Also zählt addAll die 3 Elemente (n += 3) und das von super.addAll aufgerufene add zählt jedes nochmal (n += 3) = 6. Das ist das Fragile-Base-Class-Problem: die Subklasse hängt an einem undokumentierten Implementierungsdetail der Oberklasse.

Teil 4·Quiz / Klausurfragen

Klausur-Quiz

Klausurfragen mit Lösungen (6)

F1.Wie lautet die Standard-Designregel zu Vererbung und Komposition (Bloch, Effective Java)?

Antwort: Komposition bevorzugen, Vererbung nur bei echtem IS-A und kontrollierter/dokumentierter Basisklasse

Erklärung: 'Favor composition over inheritance': Komposition ist der Default (lose gekoppelt, flexibel, kapselungssicher). Vererbung ist die begründungspflichtige Ausnahme, erlaubt bei echtem IS-A, gültigem Liskov-Prinzip und einer Basisklasse, die du kontrollierst oder die für Vererbung dokumentiert ist. Verboten ist Vererbung nicht.

F2.Welche Bedingung muss für saubere Vererbung zusätzlich zum IS-A gelten?

Antwort: Das Liskov-Substitutionsprinzip: die Subklasse ist überall einsetzbar, wo die Oberklasse erwartet wird

Erklärung: Liskov-Substitutionsprinzip: ein Objekt der Subklasse muss überall korrekt funktionieren, wo ein Objekt der Oberklasse erwartet wird, ohne das Verhalten zu brechen. Hält das nicht (klassisch Quadrat/Rechteck mit setBreite/setHoehe), ist Vererbung die falsche Wahl, auch wenn der Satz 'ist ein' sprachlich passt.

F3.'Quadrat ist ein Rechteck' rechtfertigt, dass Quadrat von Rechteck erbt, weil der IS-A-Satz stimmt.

Antwort: Falsch

Erklärung: Falsch. Sprachlich stimmt 'ist ein', aber das Verhalten bricht Liskov: ein Rechteck erlaubt setBreite und setHoehe unabhängig, beim Quadrat müssen beide gleich bleiben. Code, der für Rechtecke geschrieben ist (Breite ändern, ohne dass die Höhe folgt), liefert beim Quadrat falsche Ergebnisse. IS-A im Satz reicht nicht, das Verhalten muss substituierbar sein.

Typ: Wahr/Falsch

F4.Komposition besteht aus zwei Teilen: das andere Objekt wird als privates {{1}} gehalten, und die eigenen Methoden leiten die Arbeit per {{2}} an dieses Objekt weiter.

Lösungen pro Lücke:

  • {{1}}: Feld / Attribut / field
  • {{2}}: Delegation / Delegieren / delegation

Erklärung: Komposition = Feld + Delegation. Das gehaltene Objekt ist ein privates Feld (so bleibt die Schnittstelle minimal und gekapselt), und die eigenen Methoden delegieren (reichen die Arbeit weiter) an dieses Objekt, statt sie zu erben.

Typ: Lückentext

F5.Warum bricht Vererbung die Kapselung der Oberklasse stärker als Komposition?

Antwort: Weil die Subklasse von internen Aufrufmustern (self-use) der Oberklasse abhängt, die sich ändern können

Erklärung: Die Subklasse kann an undokumentierten Implementierungsdetails hängen, etwa daran, dass addAll intern add aufruft. Ändert die Oberklasse dieses Verhalten, bricht die Subklasse, ohne dass man sie anfasst (fragile base class). Komposition ruft nur die öffentliche Schnittstelle des gehaltenen Objekts auf und ist davon entkoppelt. (Private Felder sieht eine Subklasse übrigens NICHT direkt.)

F6.In welchem Fall ist Vererbung gegenüber Komposition vertretbar?

Antwort: Wenn eine echte IS-A-Beziehung besteht, Liskov hält und die Oberklasse für Vererbung entworfen/dokumentiert ist (oder man sie selbst kontrolliert)

Erklärung: Vererbung trägt, wenn alle drei Punkte zusammenkommen: echtes IS-A, gültiges Liskov-Prinzip und eine Basisklasse, die für Vererbung gedacht und dokumentiert ist (oder die man selbst kontrolliert, z.B. gleiches Paket). Nur Methoden einer fremden Klasse 'mitnehmen' zu wollen ist gerade das Anti-Pattern. Mehrfachvererbung über Klassen gibt es in Java ohnehin nicht.

Zur KategorieProgrammiergrundlagen.Mehr Themen entdeckenZum Themen-Hub.

UniProMax ist eine themenbasierte Lernplattform für Studierende an deutschen Unis.

Wir glauben, dass Verstehen besser ist als Auswendiglernen. Wir bauen Lerneinheiten die zeigen statt erzählen. Code, Visualisierung, Quiz. Auf Deutsch.

Marke

UniProMaxUniProMax

Themenbasiert, visuell, interaktiv.

Inhalte

  • Alle Themen (Hub)
  • Programmiergrundlagen
  • Algorithmen
  • Mathematik
  • Statistik
  • Datenbanken
  • Rechnungswesen
  • VWL

Studiengang-Filter

  • Informatik
  • Wirtschaftsinformatik
  • BWL
  • Data Science
  • VWL
  • Wirtschaftsingenieurwesen
  • Mathe
  • Psychologie
  • weitere Studiengänge folgen

Plattform

  • Mein Fortschritt
  • Impressum
  • Datenschutz
© 2026 UniProMaxAlle Systeme onlinev0.2 / Sommersemester 2026
UniProMaxUniProMaxUniProMaxUniProMax