Stand: 2012-07-05
Aufruf virtueller Methoden im Konstruktor
Überschreibbare Methoden sollten nie im Konstruktor aufgerufen werden, weil das zu Fehlverhalten führen kann.
Inhalt
Problem
Manchmal ist es verlockend, virtuelle Methoden im Konstruktor aufzurufen, um eine Basisklasse konfigurierbar zu machen. Das folgende in Java geschriebene Beispiel zeigt eine Klasse mit einer Lücke, die von einer abgeleiteten Klasse geschlossen werden soll:
abstract class Base {
Base() {
System.out.println(overridableMethod());
}
// eine konkrete Methode ohne "final" wäre genauso schlecht
abstract String overridableMethod();
}
Aber so einfach wie es aussieht, ist es nicht, denn der Konstruktor der Basisklasse Base
wird vor dem Konstruktor der abgeleiteten Klasse aufgerufen. Wenn die Methode overridableMethod()
also einen Wert verwendet, der durch den Konstruktor der abgeleiteten Klasse gesetzt wird, kommt es zu einem Fehler!
class Child extends Base {
private final String name;
Child(String name) {
this.name = name;
}
@Override String overridableMethod() {
return name; // beim Zugriff noch nicht initialisiert
}
}
Wenn mit new Child("Kind")
eine Instanz der abgeleiteten Klasse erzeugt wird, passiert Folgendes:
Base
-Konstruktor wird aufgerufen.Base
-Konstruktor ruftoverridableMethod()
auf.overridableMethod()
greift auf das Feldname
zu, bevor derChild
-Konstruktor Gelegenheit hatte, es zu initialisieren.- Die
println
-Anweisung im Konstruktor der Basisklasse gibtnull
stattKind
aus! Child
-Konstruktor wird aufgerufen, was nun aber zu spät ist.
Es spielt übrigens keine Rolle, ob die im Konstruktor aufgerufene Methode abstrakt ist oder schon in der Basisklasse eine Implementierung hat. So lange sie überschreibbar (also nicht final
) ist, bleibt das Problem bestehen.
Lösung
Die beste und einfachste Lösung ist, Konstruktorparameter zu verwenden:
abstract class Base {
Base(String name) {
System.out.println(name);
}
}
class Child extends Base {
Child(String name) {
super(name);
}
}
Wie erwartet gibt new Child("Kind")
nun Kind
aus.
Methoden, die nicht explizit für Vererbung entworfen wurden, sollten mit final
gekennzeichnet werden, um versehentliches Überschreiben zu verhindern.
Gute Entwicklungsumgebungen wie NetBeans warnen übrigens vor einem "overridable method call in constructor":
Weitere Information
- Effective Java (2nd Edition), Joshua Bloch, Prentice Hall 2008, Seite 87 ff.