Programming lesson
Vererbung und Polymorphie in Java: Ein umfassender Leitfaden für das Lab 8
Lerne die Grundlagen von Vererbung und Polymorphie in Java anhand praktischer Beispiele. Ideal für das ICS 141 Lab 8 – mit aktuellen Bezügen zu KI-Trends und Gaming.
Einführung in Vererbung und Polymorphie
In diesem Tutorial lernst du die Konzepte der Vererbung und Polymorphie in Java kennen, wie sie im ICS 141 Lab 8 behandelt werden. Diese Mechanismen sind fundamental für die objektorientierte Programmierung (OOP) und ermöglichen es dir, wiederverwendbaren, erweiterbaren Code zu schreiben. Wir nutzen aktuelle Beispiele aus der Welt der KI, des Gamings und der Finanzen, um die Konzepte greifbar zu machen.
Was ist Vererbung in Java?
Vererbung (engl. inheritance) erlaubt es einer Klasse, Eigenschaften und Methoden einer anderen Klasse zu übernehmen. Die Oberklasse (Superklasse, Elternklasse) gibt Attribute und Verhalten an die Unterklasse (Subklasse, Kindklasse) weiter. Ein einfaches Beispiel: Du hast eine Oberklasse Fahrzeug mit Attributen wie geschwindigkeit und einer Methode bewegen(). Eine Unterklasse Auto erbt diese und kann zusätzliche Methoden wie hupe() hinzufügen.
public class Fahrzeug {
protected int geschwindigkeit;
public void bewegen() {
System.out.println("Fahrzeug bewegt sich");
}
}
public class Auto extends Fahrzeug {
public void hupe() {
System.out.println("Hupe!");
}
}In diesem Beispiel erbt Auto die Methode bewegen() und das Attribut geschwindigkeit. Du kannst ein Auto-Objekt erstellen und beide Methoden aufrufen:
Auto meinAuto = new Auto();
meinAuto.bewegen(); // geerbt
meinAuto.hupe(); // eigenVererbung wird oft verwendet, um eine „ist-ein“-Beziehung abzubilden: Ein Auto ist ein Fahrzeug. Dies entspricht der Single Inheritance, bei der eine Klasse nur von einer direkten Oberklasse erben kann. Java unterstützt keine Mehrfachvererbung für Klassen (wohl aber für Interfaces).
Warum Vererbung nutzen?
Vererbung fördert die Wiederverwendbarkeit und vermeidet Code-Duplizierung. Stell dir vor, du entwickelst ein Spiel wie Fortnite oder Minecraft: Du hast eine Oberklasse Spieler mit Basisattributen wie leben und position. Unterschiedliche Spielertypen (z. B. Krieger, Magier) können die Basis überschreiben und erweitern, ohne den Code der Oberklasse zu ändern. Das spart Zeit und reduziert Fehler. In Lab 8 könntest du eine Vererbungshierarchie für geometrische Formen oder Tiere implementieren – ähnlich wie in früheren Labs, wo du vielleicht eine Shape-Klasse mit Unterklassen Circle und Rectangle erstellt hast.
Zwei Arten von Klassenbeziehungen
Die Lektüre (Kjell, Kapitel 80–82) unterscheidet zwei Hauptbeziehungen:
- „ist-ein“-Beziehung (Vererbung): Eine Unterklasse ist eine spezialisierte Form der Oberklasse. Beispiel:
Hundist einSäugetier. - „hat-ein“-Beziehung (Komposition): Eine Klasse enthält eine Referenz auf eine andere Klasse. Beispiel: Ein
Autohat einenMotor. Dies wird oft der Vererbung vorgezogen, weil es flexibler ist.
In deinem Lab 8 wirst du dich auf die „ist-ein“-Beziehung konzentrieren.
Die Wurzelklasse aller Java-Klassen
Die Klasse java.lang.Object ist die Mutter aller Klassen in Java. Jede Klasse, die du erstellst, erbt direkt oder indirekt von Object. Sie stellt grundlegende Methoden wie toString(), equals() und hashCode() bereit. Ohne explizite extends-Angabe erbt eine Klasse automatisch von Object.
Polymorphie verstehen
Polymorphie (Vielgestaltigkeit) erlaubt es, dass ein Objekt je nach Kontext unterschiedliches Verhalten zeigt. Das bedeutet: Ein Methodenaufruf kann je nach tatsächlichem Objekttyp verschiedene Implementierungen ausführen. Ein reales Beispiel: Ein Fahrzeug-Array kann sowohl Auto- als auch Fahrrad-Objekte enthalten. Ruft man die Methode bewegen() auf, führt jedes Objekt seine eigene Version aus – das Auto fährt, das Fahrrad rollt.
Fahrzeug[] fahrzeuge = new Fahrzeug[2];
fahrzeuge[0] = new Auto();
fahrzeuge[1] = new Fahrrad();
for (Fahrzeug f : fahrzeuge) {
f.bewegen(); // Polymorpher Aufruf
}Polymorphie wird oft mit Methodenüberschreibung (Overriding) realisiert: Eine Unterklasse definiert eine Methode neu, die bereits in der Oberklasse existiert. Zur Laufzeit entscheidet die JVM, welche Methode aufgerufen wird – basierend auf dem tatsächlichen Objekttyp (dynamische Bindung).
Ein aktuelles Beispiel aus der KI-Welt: Stell dir vor, du entwickelst eine App wie ChatGPT oder Midjourney. Eine Oberklasse KIModell hat eine Methode generiereAntwort(). Unterklassen wie TextModell und BildModell überschreiben diese Methode, um spezifische Ausgaben zu erzeugen. Der Code, der die Modelle nutzt, muss nur die Oberklasse kennen – Polymorphie sorgt für die richtige Ausführung.
Methodenüberladung vs. Überschreibung
Diese beiden Konzepte werden oft verwechselt.
- Überladung (Overloading): Mehrere Methoden mit demselben Namen, aber unterschiedlichen Parametern (Anzahl, Typ, Reihenfolge) in derselben Klasse. Beispiel:
add(int a, int b)undadd(double a, double b). Die Entscheidung, welche Methode aufgerufen wird, fällt zur Compile-Zeit (statische Bindung). - Überschreibung (Overriding): Eine Unterklasse definiert eine Methode neu, die bereits in der Oberklasse existiert – mit exakt derselben Signatur (Name, Parameter, Rückgabetyp). Die Entscheidung fällt zur Laufzeit (dynamische Bindung). Überschreibung ist die Grundlage für Polymorphie.
Ein Beispiel: In einer Bankkonto-Oberklasse gibt es die Methode berechneZinsen(). Die Unterklasse Sparbuch überschreibt sie mit einem höheren Zinssatz. Überladung wäre z. B. berechneZinsen(int monate) und berechneZinsen(double betrag) – beides in derselben Klasse.
Implementierung von Vererbung und Polymorphie
Für dein Lab 8 sollst du eine einfache Vererbungshierarchie implementieren und testen. Hier ein Beispiel mit Tier als Oberklasse und Hund sowie Katze als Unterklassen.
public class Tier {
public void geraeuschMachen() {
System.out.println("Tier macht ein Geräusch");
}
}
public class Hund extends Tier {
@Override
public void geraeuschMachen() {
System.out.println("Wuff!");
}
public void stoeckchenHol() {
System.out.println("Hund holt Stöckchen");
}
}
public class Katze extends Tier {
@Override
public void geraeuschMachen() {
System.out.println("Miau!");
}
}Teste den Code, indem du Objekte erstellst und Methoden aufrufst:
Tier meinTier = new Hund();
meinTier.geraeuschMachen(); // Ausgabe: Wuff!
// meinTier.stoeckchenHol(); // Compilerfehler, da Tier-Typ
Hund meinHund = new Hund();
meinHund.geraeuschMachen(); // Wuff!
meinHund.stoeckchenHol(); // Hund holt Stöckchen
Tier einTier = new Tier();
einTier.geraeuschMachen(); // Tier macht ein GeräuschRufe eine Methode der Oberklasse auf einem Unterklassen-Objekt auf – das funktioniert, weil die Unterklasse die Methode erbt. Versuche den umgekehrten Fall: Eine Methode, die nur in der Unterklasse existiert, auf einem Oberklassen-Referenztyp aufzurufen. Das führt zu einem Compilerfehler, da die Referenz nur die Methoden der Oberklasse kennt. Das liegt an der statischen Typisierung von Java.
Wenn du die @Override-Annotation verwendest, hilft dir der Compiler, Tippfehler zu vermeiden. Ohne Annotation überschreibst du die Methode trotzdem, solange die Signatur übereinstimmt.
Häufige Fehler und Tipps
- Verwechslung von Überladung und Überschreibung: Denk daran: Überladung = gleicher Name, andere Parameter; Überschreibung = gleiche Signatur, andere Klasse.
- Zugriffsmodifizierer: Überschriebene Methoden dürfen nicht schwächer sichtbar sein als die Originalmethode. Aus
protectedkannpublicwerden, aber nichtprivate. - Konstruktoren werden nicht vererbt: Du musst in der Unterklasse eigene Konstruktoren definieren und mit
super()den Konstruktor der Oberklasse aufrufen.
Zusammenfassung
Vererbung und Polymorphie sind mächtige Werkzeuge in Java. Sie ermöglichen dir, Code zu strukturieren, wiederzuverwenden und flexibel zu gestalten. In deinem Lab 8 wirst du diese Konzepte praktisch anwenden. Denk daran: Teste immer, ob die Vererbung wie erwartet funktioniert – rufe Methoden der Oberklasse auf Unterklassen-Objekten auf und umgekehrt. Viel Erfolg!