Here, weightbox is a reference to BoxWeight objects, and plainbox is a
reference to Box objects. Since BoxWeight is a subclass of Box, it is
permissible to assign plainbox a reference to the weightbox object.
It is important to understand that it is the type of the reference variable
—not the type of the object that it refers to—that determines what
members can be accessed. That is, when a reference to a subclass object is
assigned to a superclass reference variable, you will have access only to
those parts of the object defined by the superclass. This is why plainbox
can’t access weight even when it refers to a BoxWeight object. If you
think about it, this makes sense, because the superclass has no knowledge
of what a subclass adds to it. This is why the last line of code in the
preceding fragment is commented out. It is not possible for a Box
reference to access the weight field, because Box does not define one.
Although the preceding may seem a bit esoteric, it has some important
practical applications—two of which are discussed later in this chapter.
Using super
In the preceding examples, classes derived from Box were not
implemented as efficiently or as robustly as they could have been. For
example, the constructor for BoxWeight explicitly initializes the width,
height, and depth fields of Box. Not only does this duplicate code found
in its superclass, which is inefficient, but it implies that a subclass must be
granted access to these members. However, there will be times when you
will want to create a superclass that keeps the details of its
implementation to itself (that is, that keeps its data members private). In
this case, there would be no way for a subclass to directly access or
initialize these variables on its own. Since encapsulation is a primary
attribute of OOP, it is not surprising that Java provides a solution to this
problem. Whenever a subclass needs to refer to its immediate superclass,
it can do so by use of the keyword super.
super has two general forms. The first calls the superclass’ constructor.
The second is used to access a member of the superclass that has been
hidden by a member of a subclass. Each use is examined here.
Using super to Call Superclass Constructors
A subclass can call a constructor defined by its superclass by use of the
following form of super:
super(arg-list);
Here, arg-list specifies any arguments needed by the constructor in the
superclass. super( ) must always be the first statement executed inside a
subclass’ constructor.
To see how super( ) is used, consider this improved version of the
BoxWeight class:
Here, BoxWeight( ) calls super( ) with the arguments w, h, and d. This
causes the Box constructor to be called, which initializes width, height,
and depth using these values. BoxWeight no longer initializes these
values itself. It only needs to initialize the value unique to it: weight. This
leaves Box free to make these values private if desired.
In the preceding example, super( ) was called with three arguments.
Since constructors can be overloaded, super( ) can be called using any
form defined by the superclass. The constructor executed will be the one
that matches the arguments. For example, here is a complete
implementation of BoxWeight that provides constructors for the various
ways that a box can be constructed. In each case, super( ) is called using
the appropriate arguments. Notice that width, height, and depth have been
made private within Box.