Java Reference
In-Depth Information
A Subclass Object Contains the Instance Variables of All of Its Superclasses
When you create a
BasePlusCommissionEmployee
object, it contains all instance variables
declared in the class hierarchy to that point—that is, those from classes
Object
(which
does not have instance variables),
CommissionEmployee
and
BasePlusCommissionEmploy-
ee
. Class
BasePlusCommissionEmployee
does
not
inherit
CommissionEmployee
's five-ar-
gument constructor, but
explicitly invokes
it (lines 14-15) to initialize the instance variables
that
BasePlusCommissionEmployee
inherited from
CommissionEmployee
. Similarly,
Com-
missionEmployee
's constructor
implicitly
calls class
Object
's constructor.
BasePlusCom-
missionEmployee
's constructor must
explicitly
call
CommissionEmployee
's constructor
because
CommissionEmployee
does
not
have a no-argument constructor that could be in-
voked implicitly.
Testing Class
BasePlusCommissionEmployee
The
BasePlusCommissionEmployeeTest
class for this example is identical to that of
Fig. 9.7 and produces the same output, so we do not show it here. Although the version of
class
BasePlusCommissionEmployee
in Fig. 9.6 does not use inheritance and the version in
Fig. 9.9 does, both classes provide the
same
functionality. The source code in Fig. 9.9 (59
lines) is considerably shorter than that in Fig. 9.6 (127 lines), because most of the class's
functionality is now inherited from
CommissionEmployee
—there's now only one copy of
the
CommissionEmployee
functionality. This makes the code easier to maintain, modify
and debug, because the code related to a
CommissionEmployee
exists only in that class.
Notes on Using
protected
Instance Variables
In this example, we declared superclass instance variables as
protected
so that subclasses
could access them. Inheriting
protected
instance variables enables direct access to the
variables by subclasses. In most cases, however, it's better to use
private
instance variables
to encourage proper software engineering. Your code will be easier to maintain, modify
and debug.
Using
protected
instance variables creates several potential problems. First, the sub-
class object can set an inherited variable's value directly without using a
set
method. There-
fore, a subclass object can assign an invalid value to the variable, possibly leaving the object
in an inconsistent state. For example, if we were to declare
CommissionEmployee
's instance
variable
grossSales
as
protected
, a subclass object (e.g.,
BasePlusCommissionEmployee
)
could then assign a negative value to
grossSales
. Another problem with using
protected
instance variables is that subclass methods are more likely to be written so that they depend
on the superclass's data implementation. In practice, subclasses should depend only on the
superclass services (i.e., non-
private
methods) and not on the superclass data implemen-
tation. With
protected
instance variables in the superclass, we may need to modify all the
subclasses of the superclass if the superclass implementation changes. For example, if for
some reason we were to change the names of instance variables
firstName
and
lastName
to
first
and
last
, then we would have to do so for all occurrences in which a subclass
directly references superclass instance variables
firstName
and
lastName
. Such a class is
said to be
fragile
or
brittle
, because a small change in the superclass can “break” subclass
implementation. You should be able to change the superclass implementation while still
providing the same services to the subclasses. Of course, if the superclass services change,
we must reimplement our subclasses. A third problem is that a class's
protected
members