Vererbung und abstrakte Klassen
Dieses Kapitel uebertraegt das sehr umfangreiche Arbeitsblatt zur Vererbung in eine strukturierte Buchfassung. Es verbindet drei Themen, die im Blatt mehrfach ineinandergreifen:
- Spezialisierung per Vererbung (
extends) - Ueberschreiben/Ueberladen und Sichtbarkeit
- abstrakte Klassen und abstrakte Methoden
Grundidee: "ist-ein" statt "hat-ein"
Das Arbeitsblatt startet mit dem Beispiel "Kreis ist eine spezielle Ellipse". Genau das ist eine klassische ist-ein-Beziehung (is-a).
Ellipsebeschreibt Allgemeines.Kreisspezialisiert dieses Allgemeine.
In Java:
class Kreis extends Ellipse {
// zusaetzliche oder angepasste Logik
}
Vererbung bedeutet dabei nicht "Code kopieren", sondern strukturierte Wiederverwendung.
Was geerbt wird und was nicht
Wenn ClassB extends ClassA, dann uebernimmt ClassB die von ClassA geerbten Attribute und Methoden gemaess Sichtbarkeit.
Wichtig aus dem Arbeitsblatt:
- Konstruktoren werden nicht vererbt.
Das ist logisch, weil Konstruktoren immer konkret zur jeweiligen Klasse gehoeren.
Konstruktoren in Vererbungshierarchien
In einer Subklasse kann ein Konstruktor der Oberklasse mit super(...) aufgerufen werden.
Regel:
super(...)muss als erste Anweisung im Konstruktor stehen.
Beispielidee aus dem Blatt (Kreis/Ellipse):
- Ein Kreis hat einen Radius.
- Eine Ellipse hat zwei Halbachsen.
- Der Kreis-Konstruktor kann den Ellipse-Konstruktor aufrufen und zweimal denselben Radius uebergeben.
Das verhindert Duplikate und haelt Initialisierungslogik zentral.
Sichtbarkeit in der Vererbung
Das Arbeitsblatt fuehrt den Modifier protected als zentrale Stufe ein.
private: nur in der eigenen Klasse sichtbar- package-sichtbar (ohne Modifier): im selben Package sichtbar
protected: im Package plus in Subklassen sichtbarpublic: ueberall sichtbar
Fuer Vererbungsdesign ist protected oft sinnvoll, wenn Subklassen bewusst auf internere Bausteine zugreifen sollen, aber nicht die ganze Welt.
Ueberschreiben (Override)
Hat eine Subklasse eine Methode mit gleicher Signatur wie in der Oberklasse, wird in Subklassenobjekten die neue Version verwendet.
@Override
public int methode(String s, int k) {
int basis = super.methode(s, k);
return basis + 1;
}
Wichtige Punkte:
- Dynamische Bindung: Das konkrete Objekt entscheidet zur Laufzeit.
- Die Oberklassenversion kann bei Bedarf mit
super.methode(...)eingebunden werden. - Sichtbarkeit darf beim Ueberschreiben nicht unzulaessig eingeschraenkt werden.
Ueberladen (Overload) bleibt etwas anderes
Ueberladen bedeutet: gleicher Name, unterschiedliche Parameterliste.
- Beide Methoden koennen nebeneinander existieren.
- Auswahl erfolgt anhand der uebergebenen Parameter.
- Das hat nichts mit Vererbungs-Polymorphie durch Override zu tun.
Das Blatt trennt diese Begriffe bewusst, weil sie in der Praxis oft verwechselt werden.
Konstruktoren: this(...) vs super(...)
this(...): ruft einen anderen Konstruktor derselben Klasse auf.super(...): ruft einen Konstruktor der Oberklasse auf.
Beide duerfen nur als erste Anweisung stehen.
Zusatzregel aus dem Blatt:
- Wenn kein
super(...)angegeben ist, versucht Java automatisch den parameterlosen Oberklassenkonstruktor aufzurufen. - Gibt es diesen nicht, entsteht ein Compilerfehler.
Abstrakte Klassen und abstrakte Methoden
Das Arbeitsblatt zeigt ein wichtiges Entwurfsmuster: In einer Oberklasse soll eine Methode vorhanden sein, aber es gibt dort noch keine sinnvolle allgemeine Implementierung.
Dann wird sie abstrakt definiert:
protected abstract void zeichnen();
Und die Klasse muss ebenfalls abstrakt sein:
public abstract class GeometrischeFigur {
protected abstract void zeichnen();
}
Folgen:
- Von abstrakten Klassen koennen keine Objekte direkt erzeugt werden.
- Konkrete Subklassen muessen abstrakte Methoden implementieren (oder selbst abstrakt bleiben).
Warum abstrakt hier didaktisch so wichtig ist
Im Blatt wird die Figur-Hierarchie als Hauptbeispiel genutzt. Der zentrale Vorteil:
- Gemeinsame Logik (z. B. Sichtbarkeit, Bewegung) liegt in
GeometrischeFigur. - Spezifisches Zeichnen liegt in
Ellipse,Rechteck,Dreieck.
So wird Duplikatcode vermieden, ohne falsche Scheingemeinsamkeiten zu erzwingen.
Entwurfsargument aus dem Arbeitsblatt
Der Text begruendet explizit:
zeichnen()gehoert fachlich zu allen Figuren,- kann aber nicht allgemein inhaltlich implementiert werden,
- muss daher abstrakt in die Oberklasse,
- damit Methoden der Oberklasse (z. B.
setSichtbar) trotzdem aufzeichnen()zugreifen koennen.
Das ist ein sehr gutes Beispiel fuer saubere OOP-Modellierung.
Sichtbarkeit und abstrakt: kritische Kombination
Das Blatt enthaelt auch Warnungen:
- Eine abstrakte Methode darf nicht so versteckt werden, dass sie in Subklassen nicht sinnvoll implementierbar ist.
private abstractwaere im Vererbungsfluss widerspruechlich.
Darum ist protected abstract im Figurenkontext passend.
Praktische Refactoring-Hinweise (aus dem Blatt)
Das Arbeitsblatt fordert explizit, bestehende Klassen zu ueberarbeiten:
- Methoden entlang der Hierarchie verschieben ("pull up"/"push down")
- Namen konsistent anpassen
- Design und Code synchron halten
Das ist wichtig, weil gute Vererbung selten im ersten Versuch perfekt ist.
Typische Fehler in Vererbungskapiteln
- Vererbung fuer Wiederverwendung missbrauchen Nicht jede Gemeinsamkeit ist eine is-a-Beziehung.
privatein Oberklasse blockiert Subklasse Dann kann noetige Erweiterung schwer oder gar nicht erfolgen.- Override ohne
@OverrideTippfehler bleiben unbemerkt. - Konstruktorregeln ignoriert
super(...)/this(...)falsch positioniert. - Abstraktion zu frueh oder zu spaet Zu frueh: unnoetig komplex, zu spaet: viel Duplikatcode.
Uebungen zur Vertiefung
- Entwirf eine Hierarchie
GeometrischeFigur -> Rechteck/Kreis/Dreieck. - Lege gemeinsame Bewegungslogik in die Oberklasse.
- Definiere
protected abstract void zeichnen();in der Oberklasse. - Implementiere je Subklasse eine konkrete
zeichnen-Variante. - Fuege eine Methode
setSichtbar(boolean)in die Oberklasse ein und pruefe, dass siezeichnen()korrekt verwendet. - Dokumentiere, welche Mitglieder
private,protectedoderpublicsein sollen und warum.
Zusammenfassung
Das Arbeitsblatt vermittelt nicht nur Syntax, sondern Designprinzipien:
- Vererbung ist ein Modellierungswerkzeug fuer echte Spezialisierungen.
- Ueberschreiben und Ueberladen muessen sauber unterschieden werden.
- Abstrakte Klassen erlauben gemeinsame Struktur ohne falsche Scheingeneralisierung.
Wer diese Kapitelidee beherrscht, kann danach deutlich bessere Klassenhierarchien entwerfen und refaktorieren.
Vererbung nur bei echter Spezialisierung
Das Arbeitsblatt arbeitet mit Kreis/Ellipse und zeigt damit ein gutes Gegenbeispiel zu haeufigen Fehlentwuerfen: Vererbung ist kein allgemeiner Mechanismus fuer Codewiederverwendung, sondern fuer fachliche Spezialisierung.
Prueffrage vor jeder Vererbung:
- Ist jedes Objekt der Subklasse wirklich ein Spezialfall der Oberklasse?
Wenn die Antwort unsicher ist, sind oft Komposition oder Schnittstellen die bessere Wahl.
Pull-up und Push-down bewusst einsetzen
Im Blatt wird Refactoring entlang der Hierarchie explizit gefordert. Zwei typische Operationen:
- Pull-up Gemeinsame Methode aus mehreren Subklassen in die Oberklasse verschieben.
- Push-down Spezifische Methode aus Oberklasse in passende Subklassen verlagern.
Diese Operationen sind kein Selbstzweck, sondern dienen der Frage: Wo liegt fachliche Verantwortung am passendsten?
@Override als Sicherheitsnetz
Beim Ueberschreiben sollte immer @Override gesetzt werden.
@Override
protected void zeichnen() {
// konkrete Implementierung
}
Vorteil:
- Tippfehler in Signaturen werden als Compilerfehler sichtbar.
- Es ist sofort klar, welche Methoden polymorph wirken.
Dynamische Bindung konkret nachvollziehen
Das Arbeitsblatt beschreibt, dass beim Aufruf die Objektklasse entscheidend ist. Das laesst sich mit einem kleinen Test sichtbar machen:
GeometrischeFigur f = new Kreis(...);
f.zeichnen();
Obwohl Referenztyp GeometrischeFigur ist, laeuft die zeichnen-Version von Kreis. Genau das ist dynamische Bindung.
Sichtbarkeit beim Ueberschreiben
Eine ueberschriebene Methode darf nicht "enger" sichtbar werden als in der Oberklasse. Sonst waere die Ersetzbarkeit verletzt.
Beispielproblem:
- Oberklasse:
public void operation1() - Subklasse:
private void operation1()
Das ist ungueltig, weil ein oeffentlich zugesagtes Verhalten nicht versteckt werden darf.
Abstrakte Klassen mit teilweiser Implementierung
Das Blatt zeigt abstrakte Klassen nicht als reine leere Schablonen, sondern als Mischform:
- abstrakte Methoden fuer variierende Teile
- konkrete Methoden fuer gemeinsame Logik
Gerade bei Figuren ist das stark:
- gemeinsam: Position, Sichtbarkeit, Bewegung
- variabel: konkrete Zeichenlogik
So entsteht maximale Wiederverwendung ohne inhaltliche Verfaelschung.
Template-Methoden-Idee im Figurenkontext
Eine saubere Erweiterung:
public abstract class GeometrischeFigur {
private boolean sichtbar;
public final void setSichtbar(boolean sichtbar) {
this.sichtbar = sichtbar;
if (sichtbar) {
zeichnen();
} else {
loeschen();
}
}
protected abstract void zeichnen();
protected abstract void loeschen();
}
Hier ist der Ablauf fest, nur die Spezialschritte sind abstrakt. Das passt exakt zur Argumentation des Arbeitsblatts.
Konstruktorfluss in Hierarchien testen
Bei Vererbung sollten Konstruktoraufrufe sichtbar gemacht werden, z. B. mit Ausgaben. Damit erkennen Lernende:
- Oberklasse wird zuerst konstruiert.
- Dann folgt Subklasse.
super(...)steuert Parameterweitergabe.
Dieses Verstaendnis ist spaeter fuer tiefe Hierarchien entscheidend.
Typische Designfehler bei Vererbung
- Zu frueh abstrakte Oberklasse bauen.
- Zu spaet gemeinsame Logik nach oben ziehen.
- Private Felder in Oberklasse, die Subklassen fachlich brauchen.
- Ueberladene Methoden mit Override verwechselt.
- Methoden in Oberklasse, die eigentlich nur fuer eine Subklasse gelten.
Das Arbeitsblatt liefert durch seine Refactoringaufgaben genau die passende Gegenuebung.
Uebungssequenz fuer Vertiefung
- Starte mit drei getrennten Figurenklassen ohne gemeinsame Oberklasse.
- Identifiziere gleiche Methoden.
- Fuehre abstrakte
GeometrischeFigurein. - Ziehe gemeinsame Methoden per Pull-up nach oben.
- Fuehre
protected abstract void zeichnen();ein. - Teste polymorphe Aufrufe in einer Figurenliste.
So wird der Designweg nachvollziehbar statt nur das Endergebnis abgeschrieben.
Testmatrix fuer Vererbung
- Konstruktion jeder konkreten Subklasse.
- Aufruf geerbter Methoden auf Subklassenobjekten.
- Aufruf ueberschriebener Methoden ueber Oberklassenreferenz.
- Sichtbarkeitspruefung fuer
private/protected. - Sicherstellen, dass abstrakte Klassen nicht instanziiert werden.
Transfer in andere Domaenen
Das Figurenbeispiel laesst sich auf viele Kontexte uebertragen:
Fahrzeug->Auto,Fahrrad,BusDokument->PDF,Textdatei,PraesentationMitarbeiter->Lehrkraft,Verwaltung,IT
Die Leitfrage bleibt immer gleich: Welche Logik ist wirklich gemeinsam, welche ist fachlich spezifisch?
Kurzfazit zur Vertiefung
Das Arbeitsblatt vermittelt Vererbung als Modellierungswerkzeug mit Regeln:
- fachliche Spezialisierung statt Codeklauberei
- klare Trennung von Override und Overload
- abstrakte Oberklasse fuer gemeinsame Struktur
- Refactoring entlang der Hierarchie als normaler Entwicklungsprozess
Diese Perspektive verhindert viele spaetere Architekturprobleme.
Abschlussaufgabe zur Vererbung
Entwerfe eine kleine Hierarchie Dokument -> TextDokument, BildDokument, TabellenDokument.
Dokument soll abstrakt sein und enthalten:
- gemeinsame Metadaten
- abstrakte Methode
render() - konkrete Methode
druckeHeader()
Aufgaben:
- Implementiere alle Subklassen mit eigener
render-Logik. - Fuehre eine Liste vom Typ
Dokument[]und rufe polymorphrenderauf. - Erklaere, warum
Dokumentabstrakt sein sollte.
Damit wird das Figurenmuster in eine neue Domaene uebertragen und das Vererbungsverstaendnis gefestigt.
