1. Überschreiben einer Methode
Im Leben gibt es oft Situationen, in denen sich ein „Unterklasse“-Objekt besonders verhält. Zum Beispiel können alle Tiere Laute von sich geben, aber bei der Katze ist es „miau“, beim Hund „wau“ und beim Programmierer „oh, schon wieder ein Bug!“. In der Programmierung wird das durch das Überschreiben einer Methode (override) umgesetzt.
Methodenüberschreibung bedeutet, dass eine Unterklasse ihre eigene Implementierung einer Methode bereitstellt, die bereits in der Elternklasse deklariert wurde. Das heißt, sie „ersetzt“ das Standardverhalten durch ein eigenes, spezielleres.
Analogie. Wenn man sich die Elternklasse als ein Hausrezept für Borschtsch vorstellt, dann ist das Überschreiben der Methode so, als würde die Großmutter ihre geheime Zutat hinzufügen. Borschtsch bleibt Borschtsch, aber jeder hat seinen eigenen Geschmack.
Um eine Methode zu überschreiben, müssen Sie in der Kindklasse eine Methode mit genau derselben Signatur (Name, Parameter, Rückgabetyp) wie in der Elternklasse deklarieren.
Beispiel: Tiere und ihre Laute
class Animal {
void makeSound() {
System.out.println("Some generic animal sound");
}
}
class Dog extends Animal {
// Methode makeSound() überschreiben
void makeSound() {
System.out.println("Woof!");
}
}
class Cat extends Animal {
// Methode makeSound() überschreiben
void makeSound() {
System.out.println("Meow!");
}
}
Wenn Sie nun ein Objekt Dog erstellen und makeSound() aufrufen, hören Sie "Woof!" und nicht "Some generic animal sound".
Demonstration im Code
public class Main {
public static void main(String[] args) {
Animal generic = new Animal();
Dog dog = new Dog();
Cat cat = new Cat();
generic.makeSound(); // Some generic animal sound
dog.makeSound(); // Woof!
cat.makeSound(); // Meow!
}
}
Wichtig: Wenn die Unterklasse keine Methode mit derselben Signatur hat, wird die Methode der Elternklasse verwendet.
2. Annotation @Override: wozu sie dient und wie man sie verwendet
In Java ist es üblich, überschriebene Methoden mit der speziellen Annotation @Override zu kennzeichnen. Das ist nicht nur Schmuck für den Code, sondern ein hilfreiches Werkzeug:
- Der Compiler prüft, ob Sie tatsächlich eine Methode der Elternklasse überschreiben. Wenn Sie sich beim Namen, Parametertyp oder Rückgabetyp vertan haben, meldet der Compiler einen Fehler.
- Erhöht die Lesbarkeit des Codes. Ein anderer Entwickler sieht sofort: „Oh, diese Methode überschreibt die Elternmethode“.
Beispiel mit @Override
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Woof!");
}
}
Wenn Sie versehentlich void makeSond() (Tippfehler!) in einer mit @Override markierten Methode schreiben, meckert der Compiler: "Method does not override or implement a method from a supertype".
Moderne Standards. Die Verwendung von @Override gilt als gute Praxis und Branchenstandard. Auch wenn der Compiler es nicht verlangt: Setzen Sie diese Annotation immer – für Sie selbst und Ihre Kolleginnen/Kollegen wird es einfacher.
3. Wie der Aufruf einer überschriebenen Methode funktioniert
Wenn Sie eine Methode auf einem Objekt der Unterklasse aufrufen, wird die Implementierung der Unterklasse verwendet, selbst wenn die Variable als Elterntyp deklariert ist.
Beispiel: Polymorphie in Aktion
Animal animal = new Dog();
animal.makeSound(); // "Woof!", nicht "Some generic animal sound"
Hier ist die Variable vom Typ Animal, tatsächlich enthält sie aber ein Objekt Dog. Java „versteht“, dass die überschriebene Methode aus Dog aufgerufen werden soll. Das ist Polymorphie (mehr dazu in den nächsten Vorlesungen).
4. Einschränkungen und Regeln beim Überschreiben
Methodensignatur
- Name, Typ und Reihenfolge der Parameter müssen mit der Methode in der Elternklasse übereinstimmen.
- Der Rückgabetyp muss identisch oder kovariant sein (ein Subtyp des Rückgabetyps der Elternklasse). Wenn die Elternklasse beispielsweise Animal zurückgibt und die Kindklasse Dog, ist das erlaubt.
Zugriffsmodifikatoren
- Sie dürfen die Sichtbarkeit nicht strenger machen als in der Elternklasse.
- Ist die Elternmethode public, muss die überschreibende Methode ebenfalls public sein.
- Ist die Elternmethode protected, darf die überschreibende Methode protected oder public sein.
Wenn Sie versuchen, es umgekehrt zu machen, sagt der Compiler: "Cannot reduce the visibility of the inherited method".
Ausnahmen (Exceptions)
- Die überschreibende Methode darf keine neue checked‑Exception werfen, die nicht in der Deklaration der Elternmethode enthalten ist.
- Sie dürfen weniger Ausnahmen werfen als die Elternmethode oder deren Subtypen.
static, final, private
- Methoden, die als static oder final deklariert sind, sowie private Methoden (private) können nicht überschrieben werden.
- static — das ist Verbergen (hiding), kein Überschreiben.
- final — kann überhaupt nicht überschrieben werden; Java schützt solche Methoden.
- private — ist in der Unterklasse nicht sichtbar, Überschreiben ist nicht möglich (Sie können nur eine neue Methode mit demselben Namen deklarieren).
Konstruktoren
Konstruktoren werden nicht vererbt und nicht überschrieben. Jede Klasse hat ihre eigenen Konstruktoren.
5. Wir erweitern die Übungsanwendung „Zoo“
Zeit, die Theorie in die Praxis umzusetzen! Lassen Sie uns unsere „Zoo“-Anwendung weiterentwickeln.
Schritt 1. Basisklasse Animal
public class Animal {
public void makeSound() {
System.out.println("Some generic animal sound");
}
public void sleep() {
System.out.println("Zzz...");
}
}
Schritt 2. Unterklassen Dog und Cat
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
// Zusätzliche Methode nur für Dog
public void fetch() {
System.out.println("Dog brings the stick!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
// Zusätzliche Methode nur für Cat
public void scratch() {
System.out.println("Cat scratches the sofa!");
}
}
Schritt 3. Überschreiben nutzen
public class ZooTest {
public static void main(String[] args) {
Animal generic = new Animal();
Animal dog = new Dog();
Animal cat = new Cat();
generic.makeSound(); // Some generic animal sound
dog.makeSound(); // Woof!
cat.makeSound(); // Meow!
// dog.fetch(); // Fehler! Variable vom Typ Animal kennt fetch() nicht
// cat.scratch(); // Analog
// Aber wenn der Typ explizit angegeben wird:
if (dog instanceof Dog) {
((Dog) dog).fetch(); // Dog brings the stick!
}
if (cat instanceof Cat) {
((Cat) cat).scratch(); // Cat scratches the sofa!
}
}
}
Kommentar:
Die Methode makeSound() funktioniert polymorph – es wird die Version der tatsächlichen Klasse des Objekts aufgerufen. Spezifische Methoden (fetch, scratch) sind hingegen nur über eine explizite Typumwandlung zugänglich – das ist wichtig, um zu verstehen, wie Vererbung und Überschreiben funktionieren.
6. Beispiel mit Rückgabetyp (Kovarianz)
Manchmal möchte man, dass die überschreibende Methode einen „schmaleren“ Typ zurückgibt. Zum Beispiel:
class Animal {
Animal getFriend() {
return new Animal();
}
}
class Dog extends Animal {
@Override
Dog getFriend() { // Rückgabetyp — Dog, Subtyp von Animal
return new Dog();
}
}
Das nennt man Kovarianz des Rückgabetyps und ist in Java (seit Java 5) erlaubt.
7. Was passiert, wenn man @Override nicht verwendet?
Wenn Sie sich versehentlich im Methodennamen oder bei den Parametern irren, meckert Java nicht, wenn keine @Override-Annotation vorhanden ist. Infolgedessen überschreiben Sie nicht, sondern erstellen eine neue Methode, und das erwartete Verhalten ändert sich nicht.
Beispiel eines Fehlers
class Dog extends Animal {
// Tippfehler: makeSoud statt makeSound
void makeSoud() {
System.out.println("Woof!");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.makeSound(); // Gibt "Some generic animal sound" aus
}
}
Mit @Override hätte der Compiler einen Fehler gemeldet: "Method does not override or implement a method from a supertype".
8. Typische Fehler beim Überschreiben von Methoden
Fehler Nr. 1: fehlende Annotation @Override.
Ohne sie ist es leicht, sich im Methodennamen oder bei den Parametern zu vertun. Infolgedessen wird die Methode nicht überschrieben und das Programm verhält sich anders als erwartet.
Fehler Nr. 2: Versuch, den Zugriffsmodifikator zu verengen.
Wenn die Elternmethode public ist, Sie aber protected oder private schreiben, erhalten Sie einen Kompilierfehler.
Fehler Nr. 3: abweichende Signatur.
Wenn sich die Parameter schon im Typ unterscheiden, ist das kein Überschreiben mehr, sondern Überladung (overloading).
Fehler Nr. 4: Versuch, eine final- oder static-Methode zu überschreiben.
Java lässt das nicht zu: final schützt vor Überschreiben, und static-Methoden werden überhaupt nicht überschrieben (nur verborgen).
Fehler Nr. 5: den Rückgabetyp auf einen inkompatiblen ändern.
Sie dürfen nur einen Subtyp des Rückgabetyps der Elternmethode zurückgeben (Kovarianz), aber keinen völlig anderen Typ.
GO TO FULL VERSION