Inheritance and Interfaces: Chapter Goals
Inheritance and Interfaces: Chapter Goals
Inheritance and Interfaces: Chapter Goals
9
CHAPTER
Chapter Goals
◆ To learn about inheritance
341
342 Chapter 9. Inheritance and Interfaces
In the SavingsAccount class definition you specify only new methods and instance
variables. All methods and instance variables of the BankAccount class are automat-
ically inherited by the SavingsAccount class. For example, the deposit method au-
tomatically applies to savings accounts:
SavingsAccount collegeFund = new SavingsAccount(10);
collegeFund.deposit(500);
// OK to use BankAccount method with SavingsAccount object
We must introduce some more terminology here. The more general class that forms
the basis for inheritance is called the superclass. The more specialized class that in-
herits from the superclass is called the subclass. In our example, BankAccount is the
superclass and SavingsAccount is the subclass.
In Java, every class that does not specifically extend another class is a subclass of
the class Object. For example, the BankAccount class extends the class Object. The
Object class has a small number of methods that make sense for all objects, such as
the toString method that you can use to obtain a string that describes the state of
an object.
Figure 1 is a class diagram showing the relationship between the three classes
Object, BankAccount, and SavingsAccount. In this book, we use the UML no-
tation for objects and classes. (UML, for “Unified Modeling Language”, is a notation
for object-oriented analysis and design, invented by Grady Booch, Ivar Jacobson,
and James Rumbaugh, three leading researchers in object-oriented software develop-
ment.) The UML notation distinguishes between object diagrams and class diagrams.
In an object diagram the class names are underlined; in a class diagram the class
names are not underlined. In a class diagram, you denote inheritance by an arrow
with a “hollow triangle” tip that points to the superclass.
One important reason for inheritance is code reuse. By inheriting from an existing
class, you do not have to replicate the effort that went into designing and perfecting
that class. For example, when implementing the SavingsAccount class, you can rely
on the withdraw, deposit, and getBalance methods of the BankAccount class
without touching them.
9.1 An Introduction to Inheritance 343
Figure 1
Object
Inheritance Diagram
Bank
Account
Savings
Account
Let us see how our savings account objects are different from BankAccount ob-
jects. We will set an interest rate in the constructor, and then we need a method to
apply that interest periodically. That is, in addition to the three methods that can be
applied to every account, there is an additional method addInterest. These new
methods and instance variables must be defined in the subclass.
Java Syntax
9.1 Inheritance
class SubclassName extends SuperclassName
{ methods
instance variables
}
Example:
public class SavingsAccount extends BankAccount
{ public SavingsAccount(double rate)
{ interestRate = rate;
}
Purpose:
To define a new class that inherits from an existing class, and
define the methods and instance variables that are added in the
new class
Figure 2
SavingsAccount
deposit, the calls apply to the implicit parameter of the addInterest method. In
other words, the following statements are executed:
double interest = this.getBalance() * this.interestRate / 100;
this.deposit(interest);
Now the three object references stored in collegeFund, anAccount, and anObject
all refer to the same object of type SavingsAccount (see Figure 3).
However, the object variable anAccount knows less than the full story about the
object to which it refers. Because anAccount is an object of type BankAccount, you
can use the deposit and withdraw methods to change the balance of the savings
account. You cannot use the addInterest method, though—it is not a method of
the BankAccount superclass:
anAccount.deposit(1000); // OK
anAccount.addInterest();
// No—not a method of the class to which anAccount belongs
And, of course, the variable anObject knows even less. You can’t even apply the
deposit method to it—deposit is not a method of the Object class.
Why would anyone want to know less about an object and store a reference in
an object variable of a superclass? This can happen if you want to reuse code that
knows about the superclass but not the subclass. Here is a typical example. Consider
the transfer method that we discussed in Section 7.1, which transfers money from
one account to another:
void transfer(BankAccount other, double amount)
{ withdraw(amount);
other.deposit(amount);
}
You can use this method to transfer money from one bank account to another:
BankAccount momsChecking = . . . ;
BankAccount harrysChecking = . . . ;
momsChecking.transfer(harrysChecking, 1000);
Figure 3 collegeFund
anAccount
SavingsAccount
Object Variables anObject
of Different balance 0
Types Refer to
interestRate 10
the Same Object
9.2 Converting between Class Types 347
You can also use the method to transfer money into a SavingsAccount:
SavingsAccount collegeFund = . . . ;
momsChecking.transfer(collegeFund, 1000);
// OK to pass a SavingsAccount reference to a
// method expecting a BankAccount
Java Syntax
9.2 The instanceof Operator
object instanceof ClassName
Example:
Rectangle r;
if (x instanceof Rectangle)
r = (Rectangle)x;
Purpose:
To return true if the object is an instance of ClassName (or one of
its subclasses), and false otherwise
problem. At any rate, situations for casts occasionally arise. When they do, it is best
to play it safe and test whether a cast will succeed before carrying out the cast. For
that purpose, you use the instanceof operator. It tests whether an object belongs
to a particular class. For example,
anObject instanceof SavingsAccount
returns true if the type of anObject is SavingsAccount (or a subclass of Savings-
Account), false if it is not. Therefore, a safe cast can be programmed as follows:
SavingsAccount aSavingsAccount;
if (anObject instanceof SavingsAccount)
aSavingsAccount = (SavingsAccount)anObject;
else
aSavingsAccount = null;
In the real world, you often categorize concepts into hierarchies. Hierarchies are fre-
quently represented as trees, with the most general concepts at the root of the hi-
erarchy and more specialized ones towards the branches. Figure 4 shows a typical
example.
In Java it is equally common to group classes in complex inheritance hierarchies. The
classes representing the most general concepts are near the root, more specialized
classes towards the branches. For example, Figure 5 shows part of the hierarchy of
Swing user interface components in Java.
When designing a hierarchy of classes, you ask yourself which features and
behavior are common to all the classes that you are designing. Those common
properties are collected in a superclass. For example, all user interface compo-
nents have a width and height, and the getWidth and getHeight methods of
the JComponent class return the component’s dimensions. More specialized proper-
ties can be found in subclasses. For example, buttons can have text and icon labels.
9.3 Inheritance Hierarchies 349
Archosaurs
Croco-
Thecodonts Pterosaurs Dinosaurs
dilians
Sauris- Ornithis-
chians chians
Figure 4
The class AbstractButton, but not the superclass JComponent, has methods to set
and get the button text and icon, and instance variables to store them. The individ-
ual button classes (such as JButton, JRadioButton, and JCheckBox) inherit these
properties. In fact, the AbstractButton class was created to express the common-
ality between these buttons.
We will use a simpler example of a hierarchy in our study of inheritance concepts.
Consider a bank that offers its customers three kinds of accounts:
1. The checking account has no interest, gives you a small number of free trans-
actions per month, and charges a transaction fee for each additional transaction.
2. The savings account compounds interest monthly. (In our implementation,
the interest is compounded using the balance of the last day of the month,
which is somewhat unrealistic. Typically, banks use either the average or the
minimum daily balance. Exercise P9.1 asks you to implement this enhance-
ment.)
3. The time deposit account is just like a savings account, but you promise to
leave the money in the account for a particular number of months, and there
is a penalty for early withdrawal.
Let us construct an inheritance hierarchy for these account classes. All accounts have
something in common—they are bank accounts with a balance and the ability to
deposit money and (within limits) to withdraw money. They also differ in important
350 Chapter 9. Inheritance and Interfaces
JComponent
JCheckBox JRadio
Button
Figure 5
respects. The passbook savings and time deposit accounts are savings accounts, the
checking account is not. That leads us to the inheritance hierarchy of Figure 6.
Next, let us determine the behavior of these classes. All bank accounts support the
getBalance method, which simply reports the current balance. They also support
the deposit and withdraw methods, although the details of the implementation
differ. For example, a checking account must keep track of the number of transactions
to account for the transaction fees. A time deposit account must reject withdrawals
for other than the full balance.
The checking account needs a method deductFees to deduct the monthly fees
and to reset the transaction counter. The deposit and withdraw methods must be
redefined to count the transactions.
The savings accounts need a method addInterest to add interest.
Finally, the TimeDepositAccount class redefines the addInterest method,
to keep track of the number of months that the account has been open, and the
withdraw method, to collect the early withdrawal penalty when appropriate.
9.4 Inheriting Instance Variables and Methods 351
Figure 6
BankAccount
Inheritance
Hierarchy for getBalance
Bank Account deposit
withdraw
Classes
Checking Savings
Account Account
deductFees addInterest
deposit
withdraw
TimeDeposit
Account
addInterest
withdraw
Figure 6 shows which methods are implemented in each class. Following the UML
notation, methods are listed in the third compartment of the class rectangles. You will
see the details in the next section.
When you form a subclass of a given class, you can specify additional instance vari-
ables and methods. In this section, we will discuss this process in detail.
Let us look at methods first. When defining the methods for a subclass, there are
three possibilities.
1. You can override methods from the superclass. If you specify a method
with the same signature (that is, the same name and the same parame-
ter types), it overrides the method of the same name in the superclass.
Whenever the method is applied to an object of the subclass type, the
overridden method, and not the original method, is executed. For example,
CheckingAccount.deposit overrides BankAccount.deposit.
2. You can inherit methods from the superclass. If you do not explicitly override
a superclass method, you automatically inherit it. The superclass method can
352 Chapter 9. Inheritance and Interfaces
The situation for instance variables is quite different. You can never override in-
stance variables. When defining instance variables for a subclass, there are only two
cases:
1. You can inherit variables from the superclass. All instance variables from
the superclass are automatically inherited. For example, all subclasses of the
BankAccount class inherit the instance variable balance.
2. You can define new variables. Any new instance variables that you define in
the subclass are only present in subclass objects. For example, the subclass
SavingsAccount defines a new instance variable interestRate.
What happens if you define a new variable with the same name as a superclass vari-
able? For example, what happens if you define another variable named balance in
the SavingsAccount class? Then each SavingsAccount object has two instance vari-
ables of the same name (see Figure 7). The newly defined subclass variable shadows
the superclass variable. That is, the superclass variable is still present, but it cannot be
accessed. Whenever you refer to balance in a SavingsAccount method, you access
the new instance variable. Of course, if the superclass instance variable is private (as
all instance variables should be), the subclass methods aren’t able to access it any-
way. Shadowing instance variables is harmless, but it can be a source of confusion
(see Common Error 9.2).
We already implemented the BankAccount and SavingsAccount classes. Now
we will implement the subclass CheckingAccount so that you can see in detail how
methods and instance variables are inherited. Recall that the BankAccount class has
three methods and one instance variable:
public class BankAccount
{ public double getBalance() { . . . }
public void deposit(double d) { . . . }
public void withdraw(double d) { . . . }
Figure 7
SavingsAccount
Next, let us implement these methods. The deposit method increments the
transaction count and deposits the money:
public class CheckingAccount extends BankAccount
{ public void deposit(double amount)
{ transactionCount++;
// now add amount to balance
. . .
}
. . .
}
Now we have a problem. We can’t simply add amount to balance:
public class CheckingAccount extends BankAccount
{ public void deposit(double amount)
{ transactionCount++;
// now add amount to balance
balance = balance + amount; // ERROR
}
. . .
}
Although every CheckingAccount object has a balance instance variable, that in-
stance variable is private to the superclass BankAccount. Subclass methods have no
354 Chapter 9. Inheritance and Interfaces
more access rights to the private data of the superclass than any other methods. If
you want to modify a private superclass variable, you must use a public method of
the superclass, just like everyone else.
How can we add the deposit amount to the balance, using the public interface
of the BankAccount class? There is a perfectly good method for just that purpose—
namely, the deposit method of the BankAccount class. So we have to invoke
the deposit on some object. On which object? The checking account into which
the money is deposited—that is, the implicit parameter of the deposit method
of the CheckingAccount class. As you saw in Chapter 3, to invoke another method
on the implicit parameter, you don’t specify the parameter but just write the
method name:
public class CheckingAccount extends BankAccount
{ public void deposit(double amount)
{ transactionCount++;
// now add amount to balance
deposit(amount); // not complete
}
. . .
}
as
this.deposit(amount);
This version of the deposit method is correct. To deposit money into a check-
ing account, update the transaction count and then call the deposit method of the
superclass.
The remaining methods are now straightforward.
9.4 Inheriting Instance Variables and Methods 355
Java Syntax
9.3 Calling a Superclass
Method
returnType methodName(parameters)
{ . . .
super.methodName (parameters);
. . .
}
Example:
public void deposit(double amount)
{ transactionCount++;
super.deposit(amount);
}
Purpose:
To call a method of the superclass in-
stead of the method of the current class
We want to invoke the BankAccount constructor to set the balance to the initial bal-
ance. There is a special instruction to call the superclass constructor from a subclass
constructor. You use the keyword super, followed by the construction parameters
in parentheses:
public class CheckingAccount extends BankAccount
{ public CheckingAccount(double initialBalance)
{ // construct superclass
super(initialBalance);
// initialize transaction count
transactionCount = 0;
}
. . .
}
When the keyword super is followed by a parenthesis, it indicates a call to the super-
class constructor. When used in this way, the constructor call must be the first statement of
the subclass constructor. If super is followed by a period and a method name, on the other
hand, it indicates a call to a superclass method as you saw in the preceding section.
Such a call can be made anywhere in any subclass method. The dual use of the super
keyword is analogous to the dual use of the this keyword (see Advanced Topic 7.3).
If a subclass constructor does not call the superclass constructor, the superclass
is constructed with its default constructor. If the superclass does not have a default
constructor, then the compiler reports an error.
For example, you can implement the CheckingAccount constructor without
calling the superclass constructor. Then the BankAccount class is constructed
with its default constructor, which sets the balance to zero. Of course, then the
CheckingAccount constructor must explicitly deposit the initial balance.
public class CheckingAccount extends BankAccount
{ public CheckingAccount(double initialBalance)
{ // superclass has been constructed with its default constructor
// now set initial balance
super.deposit(initialBalance); // initialize transaction count
transactionCount = 0;
}
. . .
}
However, in the case of the TimeDepositAccount we have no choice. The super-
class, SavingsAccount, has no default constructor. Therefore, we must call the
superclass constructor explicitly.
public class TimeDepositAccount extends SavingsAccount
{ public TimeDepositAccount(double rate, int maturity)
{ super(rate);
periodsToMaturity = maturity;
}
. . .
}
9.6 Polymorphism 359
Java Syntax
9.4 Calling a Superclass Constructor
ClassName (parameters)
{ super(parameters);
. . .
}
Example:
public CheckingAccount(double initialBalance)
{ super(initialBalance);
transactionCount++;
}
Purpose:
To invoke the constructor of the superclass. Note
that this statement must be the first statement of
the subclass constructor.
9.6 Polymorphism
The inheritance relationship is often called the “is-a” relationship. Every object of the
subclass is also a superclass object, but with special properties. For example, every
checking account is a bank account.
Therefore, it is always possible to use a subclass object in place of a superclass
object. For example, consider again the transfer method that can be used to transfer
money from another account:
Because all our account classes extend the BankAccount class, you can pass objects
of any account class to this method:
BankAccount collegeFund = . . . ;
CheckingAccount harrysChecking = . . . ;
collegeFund.transfer(harrysChecking, 1000);
Now let us follow the method call more precisely. Inside the method call, there are
two variables of type BankAccount, but they refer to a TimeDepositAccount and a
CheckingAccount object (see Figure 8).
Consider the call to the deposit method. Which deposit method? The other
parameter has type BankAccount, so it would appear as if BankAccount.deposit
360 Chapter 9. Inheritance and Interfaces
Figure 8 this
BankAccount
Polymorphism
balance 10000
other
CheckingAccount
balance 2000
transaction 2
Count
is called. On the other hand, the CheckingAccount class provides its own deposit
method that updates the transaction count. Since the other variable actually refers
to an object of the subclass CheckingAccount, it would be appropriate if the
CheckingAccount.deposit method was called instead.
In Java, method calls are always determined by the type of the actual object, not the type
of the object reference. That is, if the actual object has the type CheckingAccount,
then the CheckingAccount.deposit method is called. It does not matter that the
object reference is stored in a variable of type BankAccount.
That means that the same statement
other.deposit(amount);
can call different methods. If the transfer method is called with a CheckingAccount
object for the other parameter, the CheckingAccount.deposit method is called.
The principle that the actual type of the object determines the method to be called
is called polymorphism. The term “polymorphism” comes from the Greek words for
“many shapes”. The same computation works for objects of many shapes, and adapts
itself to the nature of the objects. In Java, all instance methods are polymorphic.
The following program calls the polymorphic withdraw and deposit methods.
You should manually calculate what the program should print for each account bal-
ance, and confirm that the correct methods have in fact been called.
Program AccountTest.java
public class AccountTest
{ public static void main(String[] args)
9.6 Polymorphism 361
{ SavingsAccount momsSavings
= new SavingsAccount(0.5);
TimeDepositAccount collegeFund
= new TimeDepositAccount(1, 3);
CheckingAccount harrysChecking
= new CheckingAccount(0);
momsSavings.deposit(10000);
collegeFund.deposit(10000);
momsSavings.transfer.(harrysChecking, 2000);
collegeFund.transfer.(harrysChecking, 980);
harrysChecking.withdraw(500);
harrysChecking.withdraw(80);
harrysChecking.withdraw(400);
endOfMonth(momsSavings);
endOfMonth(collegeFund);
endOfMonth(harrysChecking);
another case in which the same method name can refer to different methods, namely
when a method name is overloaded: that is, when a single class has several methods
with the same name but different parameter types. For example, you can have two
constructors BankAccount() and BankAccount(double). Then the compiler selects
the appropriate method when compiling the program, simply by looking at the types
of the parameters:
account = new BankAccount();
// compiler selects BankAccount()
account = new BankAccount(10000);
// compiler selects BankAccount(double)
For another example of overloading, consider the static endOfMonth methods in
the AccountTest class. The compiler uses the type of the explicit parameter to pick
the appropriate method.
There is an important difference between polymorphism and overloading. The
compiler picks an overloaded method when translating the program, before the pro-
gram ever runs. This method selection is called early binding. However, when select-
ing the method matching the type of the implicit parameter, as in the case of the
deposit method that we just analyzed, the compiler does not make any decision
when translating the method. The program has to run before anyone can know what
is stored in other. Therefore, the virtual machine, and not the compiler, selects the
appropriate method. This method selection is called late binding.
9.7 Interfaces
Several useful Java methods in effect tell you: “If only your objects were of the
Comparable type, I would be happy to sort them for you”, or, “If you just gave me
a MouseListener, I would tell it about every mouse click.” These methods accept
parameters of type Comparable or MouseListener. If you care about sorting or
mouse listening, you have a powerful incentive to create objects that you can pass
to these methods.
For example, suppose you want to sort a collection of savings accounts by in-
creasing interest rates, to find out the best place to put your money. Normally,
you would just make the SavingsAccount class extend whatever class the sorting
method expects. But wait. The SavingsAccount class already extends another class,
BankAccount. In Java, a class cannot have two direct superclasses. In other words,
you cannot write
}
. . .
}
There is nothing wrong in principle with having more than one superclass—many
object-oriented programming languages have this capability, often called multiple in-
heritance. In those languages, though, multiple inheritance is the cause of some techni-
cal difficulties. The Java designers decided to keep the language simple and therefore
did not allow full-fledged multiple inheritance.
It turns out that one can avoid the complexities of multiple inheritance, and still
keep most of the benefits, if one limits all but one of the superclasses to a very re-
stricted form. To enable this form of inheritance, you use interfaces instead of super-
classes.
An interface is similar to a class, but there are several important restrictions:
You don’t extend interfaces; you implement them, using the special implements key-
word:
public class SavingsAccount extends BankAccount
implements Comparable
{ . . .
}
Any class that implements the Comparable interface must supply the compareTo
method.
public class SavingsAccount extends BankAccount
implements Comparable
{ . . .
public int compareTo(Object other)
{ // supply implementation
. . .
}
}
Note that the class must declare the method as public, whereas the interface does
not—all methods in an interface are public.
364 Chapter 9. Inheritance and Interfaces
Once a class implements an interface, you can convert a class reference to an in-
terface reference:
SavingsAccount harrysSavings = new SavingsAccount();
Comparable first = harrysSavings;
In particular, you can give SavingsAccount objects to methods that don’t know or
care about savings accounts but that accept parameters of type Comparable, such as
the searching and sorting methods that you will see in Chapter 15.
However, you can never construct an interface:
Comparable second; // OK
second = new Comparable(); // ERROR
9.7 Interfaces 365
Java Syntax
9.5 Defining an Interface
public interface InterfaceName
{ method signatures
}
Example:
public interface Comparable
{ int compareTo(Object other);
}
Purpose:
To define an interface and its
method signatures. The methods
are automatically public.
Java Syntax
9.6 Implementing an Interface
class SubclassName implements InterfaceName , InterfaceName , ...
{ methods
instance variables
}
Example:
public class SavingsAccount
extends BankAccount
implements Comparable
{ // other SavingsAccount methods
public int compareTo(Object other)
{ // method implementation
}
}
Purpose:
To define a new class that implements the methods of an interface
Interfaces aren’t classes, so you can’t construct interface objects. However, it is per-
fectly legal to have interface references. All interface references refer to objects of other
classes—classes that implement the interface.
A class can have only one superclass, but it can implement any number of in-
terfaces. For example, there is an interface Cloneable that we will discuss in the
next section. The SavingsAccount can implement both the Comparable and the
366 Chapter 9. Inheritance and Interfaces
Cloneable interfaces. Simply supply the names of all interfaces, separated by com-
mas, after the implements keyword:
public class SavingsAccount extends BankAccount
implements Comparable, Cloneable
{ . . .
}
Another common interface is the Shape interface. The draw and fill methods of
the Graphics2D class take a parameter of type Shape. You can draw and fill objects
of any class that implements the Shape interface, such as Line2D and Rectangle.
You will encounter more interfaces in the next chapter—they play an important role
for implementing event listeners in graphical applications.
◆ Here is an example. Suppose the First National Bank of Java decides that every account
◆ type must have some monthly fees. Therefore, a deductFees method should be added to the
◆ BankAccount class:
◆
◆ public class BankAccount
◆ { public void deductFees() { . . . }
◆ . . .
◆ }
◆
◆ But what should this method do? Of course, we could have the method do nothing. But then a
◆ programmer implementing a new subclass might simply forget to implement the deductFees
◆ method, and the new account would inherit the do-nothing method of the superclass. There
◆ is a better way: namely, to declare the deductFees method as an abstract method:
◆
◆ public abstract void deductFees();
◆ An abstract method has no implementation. This forces the implementors of subclasses to
◆ specify concrete implementations of this method. (Of course, some subclasses might decide
◆ to implement a do-nothing method, but then that is their choice—not a silently inherited
◆
default.)
◆
You cannot construct objects of classes with abstract methods. For example, once the
◆
BankAccount class has an abstract method, the compiler will flag an attempt to create a
◆
◆ new BankAccount() as an error. Of course, if the CheckingAccount subclass overrides the
◆ deductFees method and supplies an implementation, then you can create CheckingAccount
◆ objects. A class for which you cannot create objects is called an abstract class. A class for which
◆ you can create objects is sometimes called a concrete class.
◆ In Java, you must declare all abstract classes with the keyword abstract:
◆
◆ public abstract class BankAccount
◆ { public abstract void deductFees();
◆ . . .
◆ }
◆
A class that defines an abstract method, or that inherits an abstract method without over-
◆
◆ riding it, must be declared as abstract. You can also declare classes with no abstract methods
◆ as abstract. Doing so prevents programmers from creating instances of that class but allows
◆ them to create their own subclasses.
◆ Note that you cannot construct an object of an abstract class, but you can still have an object
◆ reference whose type is an abstract class. Of course, the actual object to which it refers must
◆ be an instance of a concrete subclass:
◆
◆ BankAccount anAccount; // OK
◆ anAccount = new BankAccount(); // Error— BankAccount is abstract
◆ anAccount = new SavingsAccount(); // OK
◆ anAccount = null; // OK
◆
◆ The reason for using abstract classes is to force programmers to create subclasses. The
◆ reason for abstract methods is to avoid the trouble of coming up with useless default methods
◆ and to avoid inheriting them by accident.
◆ Abstract classes differ from interfaces in an important way—they can have instance vari-
◆ ables, and they can have some concrete methods.
368 Chapter 9. Inheritance and Interfaces
Java has four levels of controlling access to variables, methods, and classes:
◆ public access
◆ private access
◆ protected access (see Advanced Topic 9.4)
◆ Package access (the default, when no access modifier is given)
We have already used the private and public modifiers extensively. Private fea-
tures can be accessed only by the methods of their own class. Public features can
be accessed by methods of all classes. We will discuss protected access in Advanced
Topic 9.4—we will not need it in this book.
9.8 Access Control 369
If you do not supply an access control modifier, then the default is package access.
That is, all methods of classes in the same package can access the feature. For exam-
ple, if a class is declared as public, then all other classes in all packages can use it.
But if a class is declared without an access modifier, then only the other classes in
the same package can use it. For that reason, the compiler must look inside all source
files of the current package, but it only needs to look for source files with the same
name as the class when locating classes from other packages. (Recall that a source
file can have at most one public class, and that it must have the same name as the file.)
Package access is a good default for classes, but it is extremely unfortunate for
variables. Instance and static variables of classes should always be private. There
are a small number of exceptions:
◆ Public constants (public static final variables) are useful and safe.
◆ Some objects, such as System.out, need to be accessible to all programs and
therefore should be public.
◆ Very occasionally, several classes in a package must collaborate very closely.
In that case, it may make sense to give some variables package access. But
inner classes are usually a better solution—you will see examples in Chapter
16.
The programmer was careless and didn’t make the variable private. There actually
was no good reason to grant package access to the warningString variable—no other
class accesses it. It is a security risk. Packages are not closed entities—any program-
mer can make a new class, add it to the java.awt package, and gain access to the
warningString variables of all Window objects!
Package access is very rarely useful, and most variables are given package access
by accident because the programmer simply forgot the private keyword and the
compiler didn’t complain—see Common Error 9.4
Methods should generally be public or private. Public methods are the norm.
Private methods make sense for implementation-dependent tasks that should be car-
ried out only by methods of the same class. Methods with package access can be
called by any other method in the same package. That can occasionally make sense
if a package consists of a small number of closely collaborating classes, but more of-
ten than not, it is simply an accident—the programmer forgot the public modifier.
We recommend that you do not use package-visible methods.
370 Chapter 9. Inheritance and Interfaces
Classes and interfaces can have public or package access. Classes that are generally
useful should have public access. Classes that are used for implementation reasons
should have package access. You can hide them even better by turning them into
inner classes; you will see examples of inner classes in Chapters 10 and 16. Inner
classes can be declared as public, private, or protected. They too have package
access if no modifier is given. There are a few examples of public inner classes, such
as the familiar Ellipse.Float and Ellipse.Double classes. However, in general,
inner classes should be private.
In Java, every class that does not extend another class automatically extends the class
Object. That is, the class Object is the direct or indirect superclass of every class in
Java (see Figure 9)
372 Chapter 9. Inheritance and Interfaces
Object
Checking Savings
Account Account
Figure 9
Of course, the methods of the Object class are very general. Here are the most
useful ones:
In fact, this toString method is called whenever you concatenate a string with an
object. Consider the concatenation
9.9 Object: The Cosmic Superclass 373
That is different from the test with the == operator. That operator tests whether the
two references are to the same object:
if (account1 == account2) . . .
// objects are the same
Let us implement the equals method for the BankAccount class. You need to
override the equals method of the Object class:
public class BankAccount
{ . . .
public boolean equals(Object otherObject)
{ . . .
}
. . .
}
Now you have a slight problem. The Object class knows nothing about bank ac-
counts, so it defines the otherObject parameter of the equals method to have the
type Object. When redefining the method, you are not allowed to change the object
signature. Of course, you can cast the parameter to the class BankAccount:
BankAccount other = (BankAccount)otherObject;
But what if someone called account.equals(x) where x wasn’t a BankAccount
object? Then the bad cast would generate an exception, and the program would die.
Therefore, you first want to test whether otherObject really is an instance of the
BankAccount class. For such a test, you use the instanceof operator. Of course, if
the test returns false, then the implicit parameter and otherObject can’t be equal
to each other, since we know that the implicit parameter has type BankAccount.
Therefore, we can formulate our test as follows:
public class BankAccount
{ . . .
public boolean equals(Object otherObject)
{ if (otherObject instanceof BankAccount)
{ BankAccount other = (BankAccount)otherObject;
return balance == other.balance;
}
else
return false;
}
. . .
}
9.9 Object: The Cosmic Superclass 375
What can you do if you actually want to make a copy of an object? That is the
purpose of the clone method. The clone method must return a new object that has
identical state to the existing object (see Figure 10). Here is a clone method for the
BankAccount class.
this
BankAccount
balance 10000
clonedAccount
BankAccount
balance 10000
Figure 10
Cloning Objects
376 Chapter 9. Inheritance and Interfaces
Since the clone method is defined in the Object class, it cannot know the type of
the object to be returned. Therefore, its return type is Object. That is not a problem
when implementing the clone method. But when you call the method, you must
use a cast to convince the compiler that account1.clone() really has the same type
as account1. Here is an example:
BankAccount account2 = (BankAccount)account1.clone();
The approach to cloning that was described here works for simple classes. For
more complex classes, there is a better way to clone an object. You can use the
Object.clone method to make a new object whose instance variables are copies
of the original. See Advanced Topic 9.5 for details.
◆ of that object reference. Figure 11 shows how the Object.clone method works if a Customer
◆ object has a reference to a BankAccount object. As you can see, the Object.clone method
◆ copies the reference to the Customer object and does not clone it. Such a copy is called a
◆ shallow copy.
◆ There is a reason why the Object.clone method does not systematically clone all sub-
◆ objects. In some situations, it is unnecessary. For example, if an object contains a reference to
◆ a string, there is no harm in copying the string reference, since Java string objects can never
◆
change their contents. The Object.clone method does the right thing if an object contains
◆
only numbers, truth values, and strings. But it must be used with caution when an object
◆
◆ contains references to other objects.
◆ For that reason, there are two safeguards built into the Object.clone method to ensure
◆ that it is not used accidentally. First, the method is declared protected (see Advanced Topic
◆ 9.4). This prevents you from accidentally calling x.clone() if the class to which x belongs
◆ hasn’t redefined clone to be public. However, when defining a clone method, you can call
◆ Object.clone. And of course you will want to make your clone method public.
◆
◆ public class BankAccount
◆ { . . .
◆ public Object clone()
◆ { // not complete
◆ Object clonedAccount = super.clone();
◆ return clonedAccount;
◆ }
◆ }
◆
◆ As a second precaution, Object.clone checks that the object being cloned implements the
◆ Cloneable interface. If not, it throws an exception. That is, the Object.clone method looks
◆ like this:
◆
◆ public class Object
◆ { protected Object clone() throws CloneNotSupportedException
◆ { if (this instanceof Cloneable)
◆ { // copy the instance variables
◆ . . .
◆ }
◆ else
◆ throw new CloneNotSupportedException();
◆
}
◆
}
◆
◆ Unfortunately, all that safeguarding means that the legitimate callers of Object.clone() pay
◆ a price—they must catch that exception even if their class implements Cloneable.
◆
◆ public class BankAccount implements Cloneable
◆ { . . .
◆ public Object clone()
◆ { try
◆ { // clones all instance variables
◆ Object clonedAccount = super.clone();
◆ return clonedAccount;
◆ }
◆
380 Chapter 9. Inheritance and Interfaces
catch(CloneNotSupportedException e)
◆
{ // can’t happen because we implement Cloneable
◆
// but we still must catch it
◆
return null;
◆
}
◆
◆ }
◆ }
◆ If an object contains a reference to another mutable object, then you need to call clone for
◆ that reference. For example, suppose the Customer class has an instance variable of class
◆ BankAccount. Then you can implement Customer.clone as follows:
◆
◆ public class Customer implements Cloneable
◆ { . . .
◆ public Object clone()
◆ { try
◆ { Customer clonedCustomer = (Customer)super.clone();
◆ clonedCustomer.account = (BankAccount)account.clone();
◆ return clonedCustomer;
◆ }
◆ catch(CloneNotSupportedException e)
◆ { // can’t ever happen because we implement Cloneable
◆ return null;
◆ }
◆
}
◆
◆
private String name;
◆
private BankAccount account;
◆
}
9.10 Packages
code is broken up so that each programmer is solely responsible for one or more
files.
As programs get even larger, however, simply distributing the classes over multiple
files isn’t enough. An additional structuring mechanism is needed. In Java, packages
provide this structuring mechanism. A Java package is a set of related classes. For
example, the Java library consists of dozens of packages, some of which are listed in
Table 1.
To put classes in a package, you must place a line
package packagename ;
as the first instruction in the source file containing the classes. As you can see from
the examples in the Java library, a package name consists of one or more identifiers
separated by periods.
For example, consider the ConsoleReader and Numeric classes that you used for
many of the programs in this book. For simplicity, you have simply included these
classes together with your own programs. However, it would be much more profes-
sional to place these classes into a separate package. That way, your own programs
are clearly separated from the book classes.
Let us place the book classes into a package named com.horstmann.ccj. (See
Section 9.10.3 for an explanation of how to construct package names.) Each source
file in that package must start with the instruction
package com.horstmann.ccj;
For example, the ConsoleReader.java file starts out as follows:
package com.horstmann.ccj;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
Naturally, that is somewhat inconvenient. You can instead import a name with an
import statement:
import java.awt.Color;
import com.horstmann.ccj.ConsoleReader;
Then you can refer to the classes as Color and ConsoleReader without the package
prefixes.
You can import all classes of a package with an import statement that ends in .*.
For example, you can use the statement
import java.awt.*;
Java Syntax
9.7 Package Specification
package packageName ;
Example:
package com.horstmann.ccj;
Purpose:
To declare that all classes in this file
belong to a particular package
9.10 Packages 383
to import all classes from the java.awt package. That statement lets you refer to
classes like Graphics2D or Color without a java.awt prefix. This is the most con-
venient method for importing classes, but if a program starts with multiple imports
of this form, it can be harder to guess to which package a class belongs. Suppose for
example a program imports
import java.awt.*;
import java.io.*;
Then suppose you see a class name Image. You would not know whether the Image
class is in the java.awt package or the java.io package. For that reason, we prefer
to use an explicit import statement for each class, though many programmers like
the convenience of importing all classes in a package.
However, you never need to import the classes in the java.lang package explic-
itly. That is the package containing the most basic Java classes, such as Math and
Object. These classes are always available to you. In effect, an automatic import
java.lang.*; statement has been placed into every source file.
Then it is up to the owner of the domain name to subdivide package names fur-
ther. For example, the Object Management Group, the holder of the omg.org do-
main name, decided to use the package name org.omg.CORBA for the Java classes
that implement their Common Object Request Broker Architecture (a mechanism
for communication between objects that are distributed on different computers).
If you don’t have your own domain name, you can still create a package name that
has a high probability of being unique, by writing your email address backwards. For
example, if Bertha M. Walters has email address bmw@cs.sjsu.edu, then she can use
a package name edu.sjsu.cs.bmw for her own classes.
can safely skip this section. If you want to add your own packages, however, or if
the compiler cannot locate a particular class or package, you need to understand the
mechanism.
A package is located in a subfolder that matches the package name. The parts of
the name between periods represent successively nested folders. For example, the
package com.horstmann.ccj would be placed in a subfolder com/horstmann/ccj.
If the package is only to be used in conjunction with a single program, then you can
place the subfolder inside the folder holding that program’s files. For example, if you
do your homework assignments in a folder ⬃/cs1hw, then you can place the class
files for the com.horstmann.ccj package into the folder ⬃/cs1hw/com/horstmann
/ccj. However, if you want to place your programs into many different folders,
such as ⬃/cs1hw/hw1, ⬃/cs1hw/hw2, . . . , then you probably don’t want to have
lots of identical subfolders ⬃/cs1hw/hw1/com/horstmann/ccj, ⬃/cs1hw/hw2/
com/ horstmann/ccj, and so on. In that case, you want to make a single folder with a
name such as ⬃/ccjbook/com/horstmann/ccj, place all class files for the package in
that directory, and tell the Java compiler once and for all how to locate the class files.
You need to add the folders that might contain packages to the class path. If you
place the com.horstmann.ccj package into a folder ⬃/ccjbook/com/horstmann/
ccj, you need to add the ⬃/ccjbook directory to that class path. The details for doing
this depend on your compilation environment. You need to consult the documenta-
tion for your compiler, or your instructor, for detailed instructions how to perform
that. If you use the Sun JDK, you may need to set the class path. The exact command
depends on the operating system. In UNIX, the command might be
setenv CLASSPATH $(HOME)/ccjbook:.
Note that the class path contains the base folders that may contain package folders.
For example, if ⬃/ccjbook is listed in the class path, then the compiler will look for
the com.horstmann.ccj package in the folder ⬃/ccjbook/com/horstmann/ccj. It is
a common error to place the complete package address in the class path. If the class
path mistakenly contains ⬃/ccjbook/com/horstmann/ccj, then the compiler will at-
tempt to locate the com.horstmann.ccj package in ⬃/ccjbook/com/horstmann/ccj/
com/horstmann/ccj and won’t find the files.
As you can see, it is a bit tricky to place class files in packages, and to set the
class path. It is a common beginner’s error to place the files in the wrong directory
or to forget setting the class path. For that reason, the classes in this book (such as
ConsoleReader) were not actually placed into packages.
To complicate matters more, sets of class files can be packaged into a single
file for efficiency. For example, you may not be able to find a /jdk/jre/lib/java/util/
Random.class file on your system. That file may be contained inside a Java archive,
called rt.jar in some systems, that is located in the /jdk/jre/lib folder. When you place
your own packages, you don’t need to worry about archives.
386 Chapter 9. Inheritance and Interfaces
Now that you have seen how packages are located, let us see how the compiler
finds a particular class. Suppose the compiler needs to locate the class com.horstmann-
. ccj.ConsoleReader. Then the compiler looks for the file com/horstmann/ccj /
ConsoleReader.class inside all directories on the class path. On the other hand,
if you have an unqualified name, such as BankAccount, then the compiler first
looks for a class BankAccount.class, but if it does not find it, it will look through
all source files in the default package to see whether any of them contains the class
BankAccount as a non-public class.
After finding a named class, the compiler checks whether the matching compiled
class file is newer than the source file. If not, it is automatically recompiled. That
way, the compiler automatically uses the most up-to-date program code.
◆ have a valid account and password. It gives each user a small slice of processor time,
◆ then serves the next user.
◆
◆ Multitasking. Even if you are the sole user of a computer, you may want to run multiple
◆
applications—for example, to read your email in one window and run the Java compiler
◆
in another. The operating system is responsible for dividing processor time between
◆
the applications you are running, so that each can make progress.
◆
◆ ◆ Printing. The operating system queues up the print requests that are sent by multiple
◆ applications. This is necessary to make sure that the printed pages do not contain a
◆ mixture of words sent simultaneously from separate programs.
◆ ◆ Windows. Many operating systems present their users with a desktop made up of mul-
◆
tiple windows. The operating system manages the location and appearance of the win-
◆
dow frames; the applications are responsible for the interior.
◆
◆ ◆ Fonts. To render text on the screen and the printer, the shapes of characters must be
◆ defined. This is especially important for programs that can display multiple type styles
◆ and sizes. Modern operating systems contain a central font repository.
◆ ◆ Communicating between programs. The operating system can facilitate the transfer of in-
◆ formation between programs. That transfer can happen through cut and paste or inter-
◆ process communication. Cut and paste is a user-initiated data transfer in which the user
◆
copies data from one application into a transfer buffer (often called a “clipboard”) man-
◆
aged by the operating system and inserts the buffer’s contents into another application.
◆
Interprocess communication is initiated by applications that transfer data without di-
◆
rect user involvement.
◆
◆ ◆ Networking. The operating system provides protocols and services for enabling appli-
◆ cations to reach information on other computers attached to the network.
◆
◆ Today, the most popular operating systems are UNIX and its variants (such as Linux), Win-
◆ dows, and the MacOS.
Chapter Summary
1. When defining a subclass, you specify added instance variables, added methods,
and changed or overridden methods.
2. When overriding a method in a subclass, you may need to call the overridden
method in the superclass. Use the super keyword.
5. To call the superclass constructor, you use the super keyword in the first statement
of the subclass constructor.
6. When you call a method, the type of the object to which the implicit parame-
ter refers determines which actual method is called. This selection process is called
polymorphism.
9. Every class inherits from the Object class. You should redefine the toString,
equals, and clone methods in classes that you create for general use.
10. The clone method makes a new object with the same value as an existing object.
11. Java packages are used to organize classes and to avoid name clashes.
12. Variables should be declared private, and methods should be declared public
or private. If the access specifier is omitted, the variable or method is accessible by
all methods of classes in the same package, which is usually not desirable.
Further Reading
[1] James Gosling, Bill Joy, and Guy Steele, The Java Language Specification, Addison-Wesley,
1996.
Review Exercises
Exercise R9.2. Describe all constructors of the SavingsAccount class. List all meth-
ods that are inherited from the BankAccount class. List all methods that are added
to the SavingsAccount class.
Exercise R9.3. Can you convert a superclass reference into a subclass reference? A
subclass reference into a superclass reference? If so, give an example. If not, explain
why.
Exercise R9.4. How does a cast of class references such as (SavingsAccount)b
differ from a cast of number values such as (int)x?
Exercise R9.5. In the following pairs of classes, identify the superclass and the sub-
class:
Employee, Manager
Polygon, Triangle
GraduateStudent, Student
Person, Student
Employee, GraduateStudent
BankAccount, CheckingAccount
Vehicle, Car
Vehicle, Minivan
Car, Minivan
Truck, Vehicle
Exercise R9.6. Suppose the class Sub extends the class Sandwich. Which of the
following assignments are legal?
Sandwich x = new Sandwich();
Sub y = new Sub();
x = y;
y = x;
y = new Sandwich();
x = new Sub();
Exercise R9.7. Draw an inheritance diagram that shows the inheritance relation-
ships between the classes
Person
Employee
390 Chapter 9. Inheritance and Interfaces
Student
Instructor
Classroom
Object
Draw an inheritance diagram that shows the relationships between these classes.
Exercise R9.9. What inheritance relationships would you establish among the fol-
lowing classes?
Student
Professor
TeachingAssistant
Employee
Secretary
DepartmentChair
Janitor
SeminarSpeaker
Person
Course
Seminar
Lecture
ComputerLab
Exercise R9.10. Which of these conditions returns true? Check the Java documen-
tation for the inheritance patterns.
Rectangle r = new Rectangle(5, 10, 20, 30);
if (r instanceof Rectangle) . . .
if (r instanceof Point) . . .
if (r instanceof Rectangle2D.Double) . . .
if (r instanceof RectangularShape) . . .
if (r instanceof Object) . . .
if (r instanceof Shape) . . .
Exercise R9.11. Explain the two meanings of the super keyword. Explain the two
meanings of the this keyword. How are they related?
Review Exercises 391
Exercise R9.13. In the AccountTest program of Section 9.4, are the calls to
the endOfMonth methods resolved by early binding or late binding? Inside the
endOfMonth(SavingsAccount) method, is the call to addInterest resolved by
early binding or late binding?
Exercise R9.14. Explain the terms shallow copy and deep copy.
Exercise R9.16. What happens if you have two public classes in a Java source file?
If you have no public classes? Try it out and explain the results.
Exercise R9.17. What happens if the name of the public class in a Java source file
doesn’t match the name of the file? Try it out and explain the results.
Exercise R9.18. Every Java program can be rewritten to avoid import statements.
Explain how, and rewrite Intersect.java from Chapter 4 to avoid import statements.
Exercise R9.19. What is the default package? Have you used it before this chapter
in your programming?
Exercise R9.20. What access attribute should instance variables have? What access
attribute should static variables have?
Exercise R9.21. What access attribute should instance methods have? Does the
same hold for static methods?
Exercise R9.22. The variables System.in and System.out are static public vari-
ables. Is it possible to overwrite them? If so, how?
392 Chapter 9. Inheritance and Interfaces
Exercise R9.23. Why are public variables dangerous? Are public static variables
more dangerous than public instance variables?
Programming Exercises
Exercise P9.2. Implement a subclass Square that extends the Rectangle class. In
the constructor, accept the x- and y-positions of the center and the side length of
the square. Call the setLocation and setSize methods of the Rectangle class.
Look up these methods in the documentation for the Rectangle class. Also supply
a method getArea that computes and returns the area of the square. Write a sample
program that asks for the center and side length, then prints out the square (using
the toString method that you inherit from Rectangle) and the area of the square.
Exercise P9.3. Implement a superclass Person. Make two classes, Student and
Instructor, inherit from Person. A person has a name and a year of birth. A stu-
dent has a major, and an instructor has a salary. Write the class definitions, the con-
structors, and the methods toString for all classes. Supply a test program that tests
these classes and methods.
Exercise P9.4. Make a class Employee with a name and salary. Make a class
Manager inherit from Employee. Add an instance variable, named department,
of type String. Supply a method toString that prints the manager’s name, depart-
ment, and salary. Make a class Executive inherit from Manager. Supply a method
toString that prints the string "Executive", followed by the information stored
in the Manager superclass object. Supply a test program that tests these classes and
methods.
Exercise P9.6. Implement a superclass Vehicle and subclasses Car and Truck. A
vehicle has a position on the screen. Write methods draw that draw cars and trucks
as follows:
Programming Exercises 393
Car Truck
Exercise P9.7. Write a method randomShape that randomly generates objects im-
plementing the Shape interface, some mixture of rectangles, ellipses, and lines, with
random positions. Call it ten times and draw all of them.
Exercise P9.8. Implement the toString, equals, and clone methods for all four
bank account classes used in this chapter.
Exercise P9.9. Implement the toString, equals, and clone methods for the Coin
and Purse classes introduced in Chapter 3.
Exercise P9.10. Reorganize the bank account classes as follows. In the BankAccount
class, introduce an abstract method endOfMonth with no implementation. Rename
the addInterest and deductFees methods into endOfMonth in the subclasses.
Which classes are now abstract and which are concrete? Write a static method void
test(BankAccount account) that makes five random transactions, prints out the
balance after each of them, then calls endOfMonth and prints the balance once again.
Test it with instances of all concrete account classes.
Exercise P9.11. Modify the Coin and Purse classes introduced in Chapter 3 to have
them implement the Comparable interface.
Exercise P9.12. Supply a class Person that implements the Comparable interface.
Compare persons by their names. Ask the user to input ten names and generate ten
Person objects. Using the compareTo method, determine the first and last person
among them and print them.
Exercise P9.13. This exercise assumes that you have an email address. Write a
Hello class in a package whose name is derived from your email address, as de-
scribed in Section 9.7.
Exercise P9.14. Reorganize the bank account classes used in this chapter by placing
them into the package com.horstmann.accounts. Leave the AccountTest program
in the default package.