Previous | Table of Contents | Next |
14.3.6.4. Subclass Constructors
In Example 14.10, we left out the constructor method for our new GraphicCircle class. Lets implement that now. Heres one way:
public GraphicCircle(double x, double y, double r, Color outline, Color fill) { this.x = x; this.y = y; this.r = r; this.outline = outline; this.fill = fill; }
This constructor relies on the fact that GraphicCircle inherits all of the variables of Circle and simply initializes those variables itself. But this duplicates the code of the Circle constructor, and if Circle did more elaborate initialization, it could become quite wasteful. Furthermore, if the Circle class had internal private fields (discussed later), we wouldnt be able to initialize them like this. What we need is a way of calling a Circle constructor from within our GraphicCircle constructor. Example 14.17 shows how we can do this.
EXAMPLE 14.17. Invoking a superclasss constructor.
public GraphicCircle(double x, double y, double r, Color outline, Color fill) { super(x, y, r); this.outline = outline; this.fill = fill; }
super is a reserved word in Java. One of its uses is that shown in the exampleto invoke the constructor method of a superclass. Its use is analogous to the use of the this keyword to invoke one constructor method of a class from within another. Using super to invoke a constructor is subject to the same restrictions as using this to invoke a constructor:
14.3.6.5. Constructor Chaining
When you define a class, Java guarantees that the classs constructor method is called whenever an instance of that class is created. It also guarantees that the constructor is called when an instance of any subclass is created. In order to guarantee this second point, Java must ensure that every constructor method calls its superclass constructor method. If the first statement in a constructor is not an explicit call to a constructor of the superclass with the super keyword, then Java implicitly inserts the call super()that is, it calls the superclass constructor with no arguments. If the superclass does not have a constructor that takes no arguments, this causes a compilation error.
There is one exception to the rule that Java invokes super() implicitly if you do not do so explicitly. If the first line of a constructor, C1, uses the this() syntax to invoke another constructor, C2, of the class, Java relies on C2 to invoke the superclass constructor, and does not insert a call to super() into C1. Of course, if C2 itself uses this() to invoke a third constructor, C2 does not call super() either, but somewhere along the chain, a constructor either explicitly or implicitly invokes the superclass constructor, which is what is required.
Consider what happens when we create a new instance of the GraphicCircle class. First, the GraphicCircle constructor shown in Example 14.17 is invoked. This constructor explicitly invokes a Circle constructor and that Circle constructor implicitly calls super() to invoke the constructor of its superclass, Object. The body of the Object constructor runs first, followed by the body of the Circle constructor, and finally followed by the body of the GraphicCircle constructor.
What this all means is that constructor calls are chainedany time an object is created, a sequence of constructor methods are invoked, from subclass to superclass on up to Object at the root of the class hierarchy. Because a superclass constructor is always invoked as the first statement of its subclass constructor, the body of the Object constructor always runs first, followed by the body of its subclass, and on down the class hierarchy to the class that is being instantiated.
14.3.6.6. The Default Constructor
There is one missing piece in the description of constructor chaining above. If a constructor does not invoke a superclass constructor, Java does so implicitly. But what if a class is declared without any constructor at all? In this case, Java implicitly adds a constructor to the class. This default constructor does nothing but invoke the superclass constructor.
For example, if we did not declare a constructor for the GraphicCircle class, Java would have implicitly inserted this constructor:
public GraphicCircle() { super(); }
Note that if the superclass, Circle(), did not declare a no-argument constructor, then this automatically inserted default constructor would cause a compilation error. If a class does not define a no-argument constructor, then all of its subclasses must define constructors that explicitly invoke the superclass constructor with the necessary arguments.
It can be confusing when Java implicitly calls a constructor or inserts a constructor definition into a classsomething is happening that does not appear in your code! Therefore, it is good coding style, whenever you
class A { int i; public A() { // Implicit call to super(); here. i = 3; } } class B extends A { // Default constructor: public B() { super(); } }
If a class does not declare any constructor, it is given a public constructor by default. Classes that do not want to be publically instantiated should declare a protected constructor to prevent the insertion of this public constructor. Classes that never want to be instantiated at all should define a private constructor.
Previous | Table of Contents | Next |