Konstruktoren von Erweiterungen in Java
Konstruktoren
Konstruktoren werden nicht vererbt.
Die Erweiterung einer Klasse übernimmt keine Konstruktoren von ihrer Basisklasse. Daher müssen alle gewünschten Konstruktoren einer Erweiterung immer ausdrücklich deklariert werden. Es wird gegebenenfalls nur ein Fehlkonstruktor generiert, wie bei allen Klassen ohne explizite Deklaration von Konstruktoren.
Main.java
final class Coordinate extends java.awt.geom.Point2D.Double
{ }public final class Main
{ public static void main( final java.lang.String[] args )
{ final Coordinate coordinate = new Coordinate( 6.0, 7.0 );
java.lang.System.out.println( coordinate ); }}transcript
Main.java:6: error: constructor Coordinate in class Coordinate cannot be applied to given types;
{ final Coordinate coordinate = new Coordinate( 6.0, 7.0 );
^
required: no arguments
found: double,double
reason: actual and formal argument lists differ in length
1 error
Beispiel: eigene RuntimeException
Main.java
final class TooManyExamplesException extends java.lang.RuntimeException {}
public final class Main
{ public static void main( final java.lang.String args[] )
{ throw new TooManyExamplesException();
java.lang.System.out.println( "println" ); }}Protokoll
Main.java:6: error: unreachable statement
java.lang.System.out.println( "println" ); }}
^
1 error
Aufrufe anderer Konstruktoren mit »this«
Aufruf des Basisklassen-Konstruktors
Ein Konstruktor einer Erweiterung kann den Konstruktor einer Basisklasse direkt am Anfang eines Konstruktorrumpfs aufrufen, indem das Schlüsselwort "super" auf eine Argumentliste angewendet wird.
- Aussprachehinweise
- super ˈsupɚ
In dem Programm "Basisklassenkonstruktor.java" ruft der parametrisierte Konstruktor der Klasse "Erweiterung" den parametrisierten Konstruktor der Basisklasse "Basis" mit "super( x )" auf.
Basisklassenkonstruktor.java
class Basis
{ Basis( double x )
{ java.lang.System.out.println( "Basis( double )" ); }}
final class Erweiterung extends Basis
{ Erweiterung( double x )
{ super( x );
java.lang.System.out.println( "Erweiterung( double )" ); }}
public final class Basisklassenkonstruktor
{ public static void main( String[] args )
{ new Erweiterung( 0. ); }}System.out
Basis( double )
Erweiterung( double )
Danach ist in diesem Konstruktorrumpf allerdings kein weiterer Konstruktoraufruf, auch kein this -Aufruf, mehr möglich, weil diese ebenfalls ganz am Anfang stehen müßte, aber diese Stelle schon besetzt ist. Deswegen wird zur Redundanzelimination, also zur Aufnahme sich wiederholender Teile mehrerer Konstruktoren, eine Methodendeklaration empfohlen.
Vorgabekonstruktion der Basisklasse
Falls ein Konstruktor nicht mit einem ausdrücklichen Konstruktoraufruf (mit dem Schlüsselwort "super" oder dem Schlüsselwort "this") beginnt, dann wird vom Übersetzer am Anfang des Konstruktorrumpfs stillschweigend die 〈ExplicitConstructorInvocation 〉 "super();" hinzugefügt. Der parameterlose Konstruktor der Basisklasse wird also dann stillschweigend gerufen.
Eventuell vorhandenen Initialisierungen von Variablen in der erweiternden Klasse (wie z.B. »int b = 5«) und der Rest des Konstruktors der erweiternden Klasse werden erst ausgeführt nachdem der Basiskonstruktor ausgeführt wurde, egal ob der Basiskonstruktor ausdrücklich oder stillschweigend aktiviert wurde (JLS3, 12.5).
In dem Programm "Basisklassenkonstruktor1.java" wird zuerst der parameterlose Konstruktor der Basisklasse ausgeführt, der das Feld "a" auf den Wert "2" setzt. Dies geschieht stillschweigend, obwohl es im Programmtext des Erweiterungskonstruktors nicht ausdrücklich verlangt wird. Anschließend setzt der Konstruktor der Erweiterung den Wert des Feldes "b" auf den Wert "4". Die folgende Ausgabe der beiden Felder legt nahe, daß tatsächlich auch der Konstruktor der Basisklasse ausgeführt wurde.
Basisklassenkonstruktor1.java
class Basis { int a; Basis() { a = 2; }}
class Erweiterung extends Basis
{ int b = 3;
Erweiterung()
{ /* stillschweigender Aufruf des parameterlosen Basiskonstruktors */
b = 4; }}
public final class Basisklassenkonstruktor1
{ public static void main( String[] args )
{ Erweiterung e = new Erweiterung();
java.lang.System.out.println( e.a );
java.lang.System.out.println( e.b ); }}System.out
2
4
In dem Programm "Basisklassenkonstruktor2.java" scheitert der stillschweigende Aufruf. Weil die Basisklasse einen expliziten Konstruktor besitzt, wird für sie nicht stillschweigend ein parameterloser Vorgabekonstruktor definiert. Damit hat sie also keinen parameterlosen Konstruktor, so daß der stillschweigende Aufruf in der Erweiterung scheitert.
Basisklassenkonstruktor2.java
class Basis
{ Basis( double x )
{ java.lang.System.out.println( "Basis( double )" ); }}
class Erweiterung extends Basis
{ Erweiterung( double x )
{ /* stillschweigender Aufruf des parameterlosen Basiskonstruktors */
java.lang.System.out.println( "Erweiterung( double )" ); }}
public final class Basisklassenkonstruktor2
{ public static void main( String[] args )
{ new Erweiterung( 0. ); }}Konsole
Test.java:11: cannot resolve symbol
symbol : constructor Basis ()
location: class Basis
{ java.lang.System.out.println( "Erweiterung( double )" ); }}
^
1 error
Nach einer geeigneten Korrektur kann das Programm übersetzt und dann disassembliert werden. Dadurch wird der stillschweigend erzeugte Aufruf des parameterlosen Basiskonstruktors in dem double -parametrisierten Konstruktor der Erweiterung sichtbar.
Konsole
javap -c Erweiterung
Compiled from Basisklassenkonstruktor2.java
class Erweiterung extends Basis {
Erweiterung(double);
}
Method Erweiterung(double)
0 aload_0
1 invokespecial #1 <Method Basis()>
4 getstatic #2 <Field java.io.PrintStream out>
7 ldc #3 <String "Erweiterung( double )">
9 invokevirtual #4 <Method void println(java.lang.String)>
12 return