Überschreiben von Methoden
Überschreiben von Methoden
- Häufig will eine Subklasse nicht exakt das Verhalten der Superklasse erben
- Subklassen können daher Methoden der Superklasse durch eigenen Implementierungen ersetzen ⇒ überschreiben (overriding)
- Die überschriebene Methode hat
- denselben Namen
- denselben Rückgabetyp
- dieselbe Parameterliste
- aber möglicherweise ein anderes Verhalten als die Methode aus der Superklasse
Beispiel: Überschreiben von Methoden
public class Mitarbeiter {
public String name;
public double gehalt;
public Date geboren;
public String getDetails() {
return name + ", geb. am " + geboren +
" hat ein Gehalt von " + gehalt;
}
}
public class Manager extends Mitarbeiter {
public String abteilung;
public String getDetails() {
return name + ", geb. am " + geburtsdatum +
" hat ein Gehalt von " + gehalt +
" und leitet die Abteilung " + abteilung;
}
}
public class Geschaeftsfuehrer extends Manager {
public boolean prokura = true;
public String getDetails() {
return name + ", geb. am " + geburtsdatum +
" hat ein Gehalt von " + gehalt +
" und leitet die Abteilung " + abteilung +
" und hat Prokura " + prokura;
}
}
Manager
und Geschaeftsfuehrer überschreiben die
getDetails()`-Methode von Mitarbeiter und passen sie an die jeweiligen Bedürfnisse der Klasse an.
Aufruf einer überschriebenen Methode
public class Manager extends Mitarbeiter {
public String abteilung;
public String getDetails() {
return super.getDetails() +
" und leitet die Abteilung " + abteilung;
}
}
public class Geschaeftsfuehrer extends Manager {
public boolean prokura = true;
public String getDetails() {
return super.getDetails() +
" und hat Prokura " + prokura;
}
}
Auch nachdem die Methode der Oberklasse überschrieben wurde, kann man noch auf sie zugreifen. Hierzu dient das Schlüsselwort super
gefolgt von dem Namen der Methode aus der Oberklasse, die man rufen möchte.
Man kann mit super
immer nur soweit die Vererbungshierarchie hinaufgehen, bis man das erste Mal auf die Methode mit dem genannten Namen trifft. Es ist nicht möglich darüber hinaus weiter nach oben zu navigieren, d. h. es gibt keine Möglichkeit aus einer Kette von mehrfach überschriebenen Methoden eine andere als die nächsthöhere aufzurufen.
Überschreiben und Zugriffskontrolle
Beim Überschreiben einer Methode kann die Sichtbarkeit nur gleich lassen oder erweitern
public class A {
protected void methode() {}
}
public class B extends A {
public void methode() {}
}
public class A {
protected void methode() {}
}
public class B extends A {
private void methode() {} // FEHLER!!
}
Sichtbarkeit wird eingeschränkt: Fehler zur Compile-Zeit.
Der Hintergrund dieser Regel wird sofort klar, wenn man sich mit der Polymorphie beschäftigt, die im nächsten Abschnitt erläutert wird.
Überschreiben und Rückgabetyp
Beim Überschreiben einer Methode kann man den Rückgabetyp nicht ändern
public class A {
protected void methode() {}
}
public class B extends A {
public void methode() {}
}
public class A {
protected void methode() {}
}
public class B extends A {
public int methode() {} // FEHLER!!
}
Seit Java 5.0 ist es möglich den Rückgabetyp einer überschriebenen Methode zu ändern solange der neue Typ eine Spezialisierung des überschriebene Typs ist. Man spricht hier von Methoden mit kovariantem Rückgabetyp (covariant return type).
Folgendes Beispiel ist korrekt für Java 5.0 und neuer, funktioniert aber nicht auf älteren Java-Releases:
class A {
}
class B extends A {
}
class C {
A getFoo() {
return new A();
}
}
class D extends C {
B getFoo() {
return new B();
}
}
Praxistipp: @Override verwenden
Überschriebene Methode mit @Override
annotieren: Der Compiler stellt dann sicher, dass wirklich eine Methode aus der Oberklasse überschrieben wird
class A {
public void method(Object o) {
// ...
}
}
class B extends A {
@Override
public void method(String o) { // FEHLER!!
// ...
}
}
Annotationen sind als Konzept an dieser Stelle zwar noch nicht bekannt, aber dieser Tipp lässt sich recht gut auch ohne detailliertes Wissen über Annotationen verwenden.
Setzt man diesen Trick konsequent ein, verhindert er, dass man aus Versehen eine Methode überlädt anstatt sie zu überschreiben oder in der Oberklasse den Namen oder die Signatur einer Methode ändert und damit die Methode der Unterklasse plötzlich gar keine vorhandene Methode der Superklasse mehr überschreibt. Da diese Art von Fehlern sehr subtil ist und damit schwer zu finden, hilft die @Override
-Annotation diese zu vermeiden.
In dem hier dargestellten Beispiel stimmt die Signatur der Methode method
in der Subklasse B
nicht mit der Signatur der Methode in der Superklasse A
überein, da in B
der Parameter von Typ String in A
aber vom Typ Object
ist. Hierdurch wird die Methode gar nicht überschrieben, sondern überladen. Die Verwendung der @Override
-Annotation sorgt dafür, dass ein Compiler-Fehler generiert wird und der Irrtum sofort entdeckt wird.
In vielen IDEs (z. B. Eclipse) kann man konfigurieren, dass die IDE automatisch beim Überschreiben einer Methode die Annotation dazu generiert bzw. warnt, wenn die Entwickler:innen sie bei einer überschriebenen Methode vergessen haben. Diese Feature sollte man auf jeden Fall nutzen.