In Chapter 3 we discussed
the basics of constructors,
including the overloading of constructors. Here we discuss some
additional aspects of constructors.
this()
When a class holds overloaded constructors, typically they include
one constructor that carries out basic initialization tasks and
then each of the other constructors does additional initialization
tasks. Rather than repeating code among constructors, a constructor
can use the this(..)
invocation to call another constructor to carry out its initialization
tasks before doing the additional ones.
For example, the following example shows a class with
two constructors:
class
Test{
int x,y;
int i,k;
Test(int x, int
y){
this.x
= x;
this.y
= y;
}
Test(int x, int
y, int i, int
k){
this(x,y);//
Must be in first line
this.i
= i;
this.k
= k;
}
}
|
The first constructor explicitly initializes the
values of two of the data variables (the other two will receive
the default 0 value for integers). The second constructor needs
to initialize the same two variables plus two more. Rather than
include redundant code, the second constructor first invokes this(x,y),
which executes the first constructor, and then initializes the
other two variables.
The argument list in the invocation of this()
must match that of the desired constructor (every constructor
must have a unique argument list in number and types). In this case,
this(x,y)
matches that of the other constructor with two int
arguments.
Also, the call to this()
must occur in the first line of the constructor.
super()
For subclasses, a constructor of that class plus a constructor
in each of its super-classes will be invoked (we discuss below the
sequence of constructors). Similar to the this()
operator, we can access particular constructors in the super-class
using super().
For example, in the following code, class Test2
extends class Test1,
which holds two constructors. The constructor in class Test2
invokes the first constructor in class Test1
using super(int).
|
class
Test1 {
int i;
int j;
Test1(int i)
{this.i=i;}
Test1(int i,
int j)
{
this.i=i;
this.j=j;
}
}
class Test2 extends
Test1 {
int k;
Test2(int i,
int j, int k){
super(i,j);//
Must be in
//
first line
this.k=k;
}
}
|
As with this(),
the argument list will identify which of the constructors in
the super-class to invoke. Also, as with this(),
the super()
must lie in the first line of the constructor.
Construction Sequence
When you instantiate a subclass, the object will
begin with an invocation of the constructor in the base class
and initialize downwards through constructors in each
subclass till it reaches the final subclass constructor.
Note: This
top down imagery for class inheritance, rather than a upward
tree-like approach, is standard in OOP but is sometimes confusing
to newcomers.
The question then arises: if one or more of the
super-classes have multiple constructors, which constructor
does the JVM invoke?
Let's begin with the simplest case of a super-class
definition without any constructors other than a zero argument
constructor (or the default zero argument constructor). In this
case, the subclass constructor does not need to invoke super().
The JVM will automatically invoke the default zero argument
constructor in the super-class.
If the super-class holds one or more non-zero
argument constructors and does not include a zero argument constructor,
then the subclass must employ a super(...)
with an argument list matching that of the desired super-class
constructor. Otherwise, the compiler will give an error message
since it doesn't know which super-class constructor to invoke.
Note that if, in this case, the subclass also holds several
constructors, each can invoke a different super()
to a different super-class constructor. The compiler and JVM
will figure out the proper sequence of constructors to call
as it builds the instance of the subclass according to which
constructor was used with the new
operator.
Demo 3 provides an example of a
sequence of constructors invoked for the case of a base class
and two subclasses, all with overloaded constructors.
|