The super() Constructor Call
The constructor call super() is used in a subclass constructor to invoke a constructor in the direct superclass. This allows the subclass to influence the initialization of its inherited state when an object of the subclass is created. A super() call in the constructor of a subclass will result in the execution of the relevant constructor from the superclass, based on the signature of the call. Since the superclass name is known in the subclass declaration, the compiler can determine the superclass constructor invoked from the signature of the parameter list.
A constructor in a subclass can access the class’s inherited members by their simple names. The keyword super can also be used in a subclass constructor to access inherited members via its superclass. One might be tempted to use the super keyword in a constructor to specify initial values for inherited fields. However, the super() construct provides a better solution to initialize the inherited state.
In Example 5.7, the constructor at (3) of the class Light has a super() call (with no arguments) at (4). Although the constructor is not strictly necessary, as the compiler will insert one—as explained later—it is included here for expositional purposes. The constructor at (6) of the class TubeLight has a super() call (with three arguments) at (7). This super() call will match the constructor at (3) of the superclass Light. This is evident from the program output.
Example 5.7 The super() Constructor Call
// File: ChainingConstructors.java
class Light {
// Fields:
private int noOfWatts;
private boolean indicator;
private String location;
// Constructors:
Light() { // (1) No-argument constructor
this(0, false);
System.out.println(
“Returning from no-argument constructor no. 1 in class Light”);
}
Light(int watt, boolean ind) { // (2)
this(watt, ind, “X”);
System.out.println(
“Returning from constructor no. 2 in class Light”);
}
Light(int noOfWatts, boolean indicator, String location) { // (3)
super(); // (4)
this.noOfWatts = noOfWatts;
this.indicator = indicator;
this.location = location;
System.out.println(
“Returning from constructor no. 3 in class Light”);
}
}
//______________________________________________________________________________
class TubeLight extends Light {
// Instance variables:
private int tubeLength;
private int colorNo;
// Constructors:
TubeLight(int tubeLength, int colorNo) { // (5)
this(tubeLength, colorNo, 100, true, “Unknown”);
System.out.println(
“Returning from constructor no. 1 in class TubeLight”);
}
TubeLight(int tubeLength, int colorNo, int noOfWatts,
boolean indicator, String location) { // (6)
super(noOfWatts, indicator, location); // (7)
this.tubeLength = tubeLength;
this.colorNo = colorNo;
System.out.println(
“Returning from constructor no. 2 in class TubeLight”);
}
}
//______________________________________________________________________________
public class ChainingConstructors {
public static void main(String[] args) {
System.out.println(“Creating a TubeLight object.”);
TubeLight tubeLightRef = new TubeLight(20, 5); // (8)
}
}
Output from the program:
Creating a TubeLight object.
Returning from constructor no. 3 in class Light
Returning from constructor no. 2 in class TubeLight
Returning from constructor no. 1 in class TubeLight
The super() construct has the same restrictions as the this() construct: If used, the super() call must occur as the first statement in a constructor, and it can only be used in a constructor declaration. This implies that this() and super() calls cannot both occur in the same constructor. The this() construct is used to chain constructors in the same class. The constructor at the end of such a chain can invoke a superclass constructor using the super() construct. Just as the this() construct leads to chaining of constructors in the same class, so the super() construct leads to chaining of subclass constructors to superclass constructors. This chaining behavior guarantees that all superclass constructors are called, starting with the constructor of the class being instantiated, all the way to the top of the inheritance hierarchy, which is always the Object class. Note that the body of the constructor is executed in the reverse order to the call order, as the super() call can occur only as the first statement in a constructor. This order of execution ensures that the constructor from the Object class is completed first, followed by the constructors in the other classes down to the class being instantiated in the inheritance hierarchy. This is called (subclass–superclass) constructor chaining. The output from Example 5.7 clearly illustrates this chain of events when an object of the class TubeLight is created.
If a constructor at the end of a this() chain (which may not be a chain at all if no this() call is invoked) does not have an explicit call to super(), the call super() (without the parameters) is implicitly inserted by the compiler to invoke the no-argument constructor of the superclass. In other words, if a constructor has neither a this() call nor a super() call as its first statement, the compiler inserts a super() call to the no-argument constructor in the superclass. The code
class A {
A() {} // No-argument constructor.
// …
}
class B extends A { // No constructors.
// …
}
is equivalent to
class A {
A() { super(); } // (1) Call to no-argument superclass constructor inserted.
// …
}
class B extends A {
B() { super(); } // (2) Default constructor inserted.
// …
}
where the compiler inserts a super() call in the no-argument constructor for class A at (1) and inserts the default constructor for class B at (2). The super() call at (2) will result in a call to the no-argument constructor in A at (1), and the super() call at (1) will result in a call to the no-argument constructor in the superclass of A—that is, the Object class.
If a superclass defines just non-zero argument constructors (i.e., only constructors with parameters), its subclasses cannot rely on the implicit super() call being inserted. This will be flagged as a compile-time error. The subclasses must then explicitly call a superclass constructor, using the super() construct with the right arguments.
class NeonLight extends TubeLight {
// Field
private String sign;
NeonLight() { // (1)
super(10, 2, 100, true, “Roof-top”); // (2) Cannot be commented out.
sign = “All will be revealed!”;
}
// …
}
The preceding declaration of the subclass NeonLight provides a no-argument constructor at (1). The call of the constructor at (2) in the superclass TubeLight cannot be omitted. If it is omitted, any insertion of a super() call (with no arguments) in this constructor will try to match a no-argument constructor in the superclass Tube-Light, which provides only non-zero argument constructors. The class NeonLight will not compile unless an explicit valid super() call is inserted at (2).
If the superclass provides just non-zero argument constructors (i.e., it does not have a no-argument constructor), this has implications for its subclasses. A subclass that relies on its default constructor will fail to compile because the default constructor of the subclass will attempt to call the (nonexistent) no-argument constructor in the superclass. A constructor in a subclass must explicitly use the super() call, with the appropriate arguments, to invoke a non-zero argument constructor in the superclass. This call is necessary because the constructor in the subclass cannot rely on an implicit super() call to the no-argument constructor in the superclass.