Inheritance and Interfaces: Chapter Goals

Download as pdf or txt
Download as pdf or txt
You are on page 1of 54

Inheritance and Interfaces

9
CHAPTER

Chapter Goals
◆ To learn about inheritance

◆ To be able to convert between supertype and subtype references

◆ To understand how to inherit and override superclass methods

◆ To understand the concept of polymorphism

◆ To design and use abstract classes and interfaces

◆ To understand the common superclass Object and to override its toString,


equals, and clone methods

◆ To learn about packages and Java access control

341
342 Chapter 9. Inheritance and Interfaces

9.1 An Introduction to Inheritance

Inheritance is a mechanism for enhancing existing, working classes. If you need to


implement a new class and a class representing a more general concept is already
available, then the new class can inherit from the existing class. For example, sup-
pose you need to define a class SavingsAccount to model an account that pays a
fixed interest rate on deposits. You already have a class BankAccount, and a savings
account is a special case of a bank account. In this case, it makes sense to use the
language construct of inheritance. Here is the syntax for the class definition:
class SavingsAccount extends BankAccount
{ new methods
new instance variables
}

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.

public class SavingsAccount extends BankAccount


{ public SavingsAccount(double rate)
{ constructor implementation
}

public void addInterest()


{ method implementation
}

private double interestRate;


}

Figure 2 shows the layout of a SavingsAccount object. It inherits the balance


instance variable from the BankAccount superclass, and it gains one additional in-
stance variable: interestRate.
Next, you need to implement the new addInterest method. This method com-
putes the interest due on the current balance and deposits that interest to the account.

public class SavingsAccount extends BankAccount


{ public SavingsAccount(double rate)
{ interestRate = rate;
}
344 Chapter 9. Inheritance and Interfaces

Java Syntax
9.1 Inheritance
class SubclassName extends SuperclassName
{ methods
instance variables
}

Example:
public class SavingsAccount extends BankAccount
{ public SavingsAccount(double rate)
{ interestRate = rate;
}

public void addInterest()


{ double interest = getBalance() * interestRate / 100;
deposit(interest);
}

private double interestRate;


}

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

Layout of a balance 10000 BankAccount portion


Subclass Object
interestRate 10

public void addInterest()


{ double interest = getBalance() * interestRate / 100;
deposit(interest);
}

private double interestRate;


}
Note how the addInterest method calls the getBalance and deposit meth-
ods of the superclass. Because no object is specified for the calls to getBalance and
9.2 Converting between Class Types 345

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);

For example, if you call


collegeFund.addInterest();

then the following instructions are executed:


double interest = collegeFund.getBalance() *
collegeFund.interestRate / 100;
collegeFund.deposit(interest);

Common Error 9.1


◆ Confusing Super- and Subclasses

◆ If you compare an object of type SavingsAccount with an object of type BankAccount, then
◆ you find that

◆ ◆ The keyword extends suggests that the SavingsAccount object is an extended ver-
◆ sion of a BankAccount.
◆ ◆ The SavingsAccount object is larger; it has an added instance variable interestRate.

◆ ◆ The SavingsAccount object is more capable; it has an addInterest method.

◆ It seems a superior object in every way. So why is SavingsAccount called the subclass and
◆ BankAccount the superclass?
◆ The super/sub terminology comes from set theory. Look at the set of all bank accounts. Some
◆ of them are plain bank accounts, and some of them are SavingsAccount objects. Therefore,
◆ the set of BankAccount objects is a superset of the set of SavingsAccount objects, and the
◆ set of SavingsAccount objects is a subset of the set of all BankAccount objects. The more
◆ specialized objects in the subset have richer state and more capabilities.

9.2 Converting between


Class Types

The class SavingsAccount extends the class BankAccount. In other words, a


SavingsAccount object is a special case of a BankAccount object. Therefore, you
346 Chapter 9. Inheritance and Interfaces

can store a reference to a SavingsAccount object into an object variable of type


BankAccount:

SavingsAccount collegeFund = new SavingsAccount(10);


BankAccount anAccount = collegeFund;
Object anObject = collegeFund;

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

The transfer method expects a reference to a BankAccount, and it gets a reference


to the subclass SavingsAccount. Fortunately, rather than complaining about a type
mismatch, the compiler simply copies the subclass reference collegeFund to the
superclass reference other. The transfer method doesn’t actually know that, in
this case, other refers to a SavingsAccount reference. It knows only that other is
a BankAccount, and it doesn’t need to know anything else. All it cares about is that
the other object can carry out the deposit method.
However, while you can convert an object reference into a superclass reference,
you cannot convert between unrelated classes:
Rectangle r = collegeFund; // Error—unrelated classes

The compiler knows that Rectangle is not a superclass of SavingsAccount, and it


refuses to compile this statement.
Occasionally, it happens that you convert an object to a superclass reference and
you need to convert it back. Suppose you captured a reference to a savings account
in a variable of type Object:
Object anObject = collegeFund;
Much later, you want to add interest to the account, but you no longer have ac-
cess to the original object variable. You cannot simply assign a supertype reference
to a subtype reference. However, as long as you are absolutely sure that anObject
really refers to a SavingsAccount object, you can use the cast notation to convert
it back:
SavingsAccount aSavingsAccount = (SavingsAccount)anObject;
If you are wrong, and the object doesn’t actually refer to a savings account, your
program will throw an exception and terminate.
This cast notation is the same notation that you saw in Chapter 2 to convert be-
tween number types. For example, if x is a floating-point number, then (int)x is
the integer part of the number. The intent is similar—to convert from one type to
another. However, there is one big difference between casting of number types and
casting of class types. When casting number types, you lose information, and you use
the cast to tell the compiler that you agree to the information loss. When casting
object types, on the other hand, you take a risk—namely, that the object type wasn’t
actually of the type that you thought it was and that the program will be terminated
as a result, and you tell the compiler that you agree to that risk.
You saw one example of using casts in graphics programs, when you had to cast
the Graphics object to a Graphics2D object. That cast is really not a sign of good
programming; the library designers used the cast as a quick fix for a compatibility
348 Chapter 9. Inheritance and Interfaces

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;

9.3 Inheritance Hierarchies

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

A Part of the Hierarchy of


Ancient Reptiles

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

JPanel JText JLabel Abstract


Component Button

JTextField JTextArea JToggle JButton


Button

JCheckBox JRadio
Button

Figure 5

A Part of the Hierarchy of Swing


User Interface Components

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.

9.4 Inheriting Instance Variables


and Methods

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

be applied to the subclass objects. For example, the CheckingAccount class


inherits the BankAccount.getBalance method.
3. You can define new methods. If you define a method that did not exist in
the superclass, then the new method can be applied only to subclass objects.
For Example, CheckingAccount.deductFees is a new method that does not
exist in the superclass BankAccount.

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

Shadowing Instance balance 10000 BankAccount portion


Variables
interestRate 5
balance 0
9.4 Inheriting Instance Variables and Methods 353

private double balance;


}
The CheckingAccount has an added method deductFees and an added instance
variable transactionCount, and it overrides the deposit and withdraw methods
to increment the transaction count:
public class CheckingAccount extends BankAccount
{ public void deposit(double d) { . . . }
public void withdraw(double d) { . . . }
public void deductFees() { . . . }

private int transactionCount;


}
Each object of class CheckingAccount has two instance variables:

balance (inherited from BankAccount)


transactionCount (new to CheckingAccount)

You can apply four methods to CheckingAccount objects:

getBalance() (inherited from BankAccount)


deposit(double) (overrides BankAccount method)
withdraw(double) (overrides BankAccount method)
deductFees() (new to CheckingAccount)

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
}
. . .
}

But this won’t quite work. The compiler interprets


deposit(amount);

as
this.deposit(amount);

The this parameter is of type CheckingAccount. There is a method called deposit


in the CheckingAccount class. Therefore, that method will be called—but that is just
the method we are currently writing! The method will call itself over and over, and
the program will die in an infinite recursion.
Instead, we must be more specific that we want to invoke only the superclass’s
deposit method. There is a special keyword super for this purpose:

public class CheckingAccount extends BankAccount


{ public void deposit(double amount)
{ transactionCount++;
// now add amount to balance
super.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

public class CheckingAccount extends BankAccount


{ . . .
public void withdraw(double amount)
{ transactionCount++;
// now subtract amount from balance
super.withdraw(amount);
}

public void deductFees()


{ if (transactionCount > FREE_TRANSACTIONS)
{ double fees = TRANSACTION_FEE
* (transactionCount - FREE_TRANSACTIONS);
super.withdraw(fees);
}
transactionCount = 0;
}
. . .
private static final int FREE_TRANSACTIONS = 3;
private static final double TRANSACTION_FEE = 2.0;
}

Next, we will turn to the implementation of the TimeDepositAccount, a sub-


class of the SavingsAccount class. In a time deposit account, the account holder
promises to leave the money in the account for some number of months, presum-
ably in exchange for a higher interest rate. We store this number as the maturity of the
account. The value is set in the constructor—see the next section for the constructor
implementation. Once the account has matured, there is no withdrawal penalty.
356 Chapter 9. Inheritance and Interfaces

public class TimeDepositAccount extends SavingsAccount


{ . . .
public void addInterest()
{ periodsToMaturity--;
super.addInterest();
}

public void withdraw(double amount)


{ if (periodsToMaturity > 0)
super.withdraw(EARLY_WITHDRAWAL_PENALTY);
super.withdraw(amount);
}

private int periodsToMaturity;


private static final double EARLY_WITHDRAWAL_PENALTY = 20.0;
}
Note that the TimeDepositAccount is two levels removed from the BankAccount
class. The BankAccount class is a superclass, but not the immediate superclass. The
TimeDepositAccount class still inherits two methods from the BankAccount class,
namely getBalance and deposit (see Figure 6). Methods can be inherited from an
indirect superclass as long as none of the intermediate superclasses overrides them.
Consider the method calls super.addInterest and super.withdraw in the
addInterest and withdraw methods of the TimeDepositAccount class. Which
methods do they call? The keyword super indicates that they call the methods of the
superclass—that is, SavingsAccount. In the case of the addInterest method that
is true, because the SavingsAccount class actually has an addInterest method.
The SavingsAccount class doesn’t have a withdraw method, though; it inherits
that method from the BankAccount class (again, see Figure 6). Therefore, super.
withdraw actually calls BankAccount.withdraw. In general, the closest method in
the inheritance hierarchy is called.

Common Error 9.2


◆ Shadowing Instance Variables

◆ A subclass has no access to the private instance variables of the superclass. For example, the
◆ methods of the CheckingAccount class cannot access the balance variable:
◆ public class CheckingAccount extends BankAccount
◆ { public void deposit(double amount)
◆ { transactionCount++;
◆ balance = balance + amount; // ERROR
◆ }
◆ . . .
◆ }

◆ It is a common beginner’s error to “solve” this problem by adding another instance variable
◆ with the same name.
9.5 Subclass Construction 357

public class CheckingAccount extends BankAccount



{ public void deposit(double amount)

{ transactionCount++;

balance = balance + amount;

}

. . .

private double balance;

}

◆ Sure, now the deposit method compiles, but it doesn’t update the correct balance! Such a
◆ CheckingAccount object has two instance variables, both named balance (see Figure 7). The
◆ getBalance method of the superclass retrieves one of them, and the deposit method of the
◆ subclass updates the other.

Common Error 9.3


◆ Overriding Superclass Methods

◆ A common error in extending the functionality of a superclass method is to forget the super.
◆ qualifier. For example, to withdraw money from a checking account, update the transaction
◆ count and then withdraw the amount:
◆ public void withdraw(double amount)
◆ { transactionCount++;
◆ withdraw(amount); // Error—should be super.withdraw(amount)
◆ }

◆ Here withdraw(amount) refers to the withdraw method applied to the implicit parameter of
◆ the method. The implicit parameter is of type CheckingAccount, and the CheckingAccount
◆ class has a withdraw method, so that method is called. Of course, that is a recursive call to
◆ the current method. Instead, you must be precise which withdraw method you want to call.
◆ Another common error is to forget to call the superclass method altogether. Then the func-
◆ tionality of the superclass mysteriously vanishes.

9.5 Subclass Construction

Let us define a constructor to set the initial balance of a checking account.


public class CheckingAccount extends BankAccount
{ public CheckingAccount(double initialBalance)
{ // construct superclass
. . .
// initialize transaction count
transactionCount = 0;
}
. . .
}
358 Chapter 9. Inheritance and Interfaces

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:

public void transfer(BankAccount other, double amount)


{ withdraw(amount);
other.deposit(amount);
}

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);

printBalance("mom's savings", momsSavings);


// $10000 – $2000 + 0.5% interest = $8040
printBalance("the college fund", collegeFund);
// $10000 – $980 – $20 penalty + 1% interest
// = $9090
printBalance("Harry's checking", harrysChecking);
// $2000 + $980 – $500 – $80 – $400 – $4 fees
// = $1996
}

public static void endOfMonth(SavingsAccount savings)


{ savings.addInterest();
}

public static void endOfMonth(CheckingAccount checking)


{ checking.deductFees();
}
public static void printBalance(String name,
BankAccount account)
{ System.out.println("The balance of " + name
+ " account is $" + account.getBalance());
}
}

When you see a polymorphic method call, such as other.deposit(amount),


there are several possible deposit methods that can be called. You have already seen
362 Chapter 9. Inheritance and Interfaces

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

public class SavingsAccount


extends BankAccount, Comparable // ERROR
{ public int compareTo(Object other)
{ . . .
9.7 Interfaces 363

}
. . .
}

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:

◆ An interface does not have instance variables.


◆ All methods in an interface are abstract; that is, they have a name, parameters,
and a return type, but they don’t have an implementation.
◆ All methods in an interface are automatically public.

For example, the java.lang package defines a Comparable interface as follows:


public interface Comparable
{ int compareTo(Object other); // no implementation
}

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

The compareTo method is intended for sorting collections of objects. A sorting


method will repeatedly call
first.compareTo(second)
and decide, depending on the return value, whether the objects are in the correct
order or whether they need to be rearranged. (You will see in Chapter 15 how to
implement such a sorting method.) Of course, when sorting numbers, the sorting
method can just see which number is larger. But how can it sort bank accounts or
rectangles? The sorting method itself has no knowledge of these objects and their
meaning. The sorting method therefore declares, “I am willing to sort a collection of
objects, provided they implement the Comparable interface.”
Why doesn’t every class have a compareTo method? Comparison doesn’t make
sense for all objects. You can sort savings accounts by their interest rates, but there
is no natural way for sorting PrintStream or Graphics2D objects. Thus, by default,
objects of arbitrary classes are not comparable. A developer must want a class to
be comparable and demonstrate that desire by implementing the Comparable inter-
face.
Let us finally provide the code for the compareTo method of the SavingsAccount
class. The compareTo method should return zero if two objects are identical, a neg-
ative number if the first one should come before the second when the objects are
sorted, and a positive number if the first one should come after the second. The ex-
act value of the returned integer does not matter; only the sign (+ or -) counts.
public class SavingsAccount
extends BankAccount implements Comparable
{ public int compareTo(Object other)
{ SavingsAccount otherAccount = (SavingsAccount)other;
if (interestRate < otherAccount.interestRate) return -1;
if (interestRate > otherAccount.interestRate) return 1;
return 0;
}
. . .
}

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.

Advanced Topic 9.1


◆ Constants in Interfaces

◆ Interfaces cannot have variables, but you can specify constants, which will be inherited by all
◆ classes that implement the interface.
◆ For example, the SwingConstants interface defines various constants such as
◆ SwingConstants.NORTH, SwingConstants.EAST, and so on. Several classes implement this
◆ interface. For example, since JLabel implements the SwingConstants interface, users can
◆ refer to them as JLabel.NORTH, JLabel.EAST, and so on, when using these constants in con-
◆ junction with JLabel objects.
◆ When defining a constant in an interface, you can (and should) omit the keywords public
◆ static final, because all variables in an interface are automatically public static final.
◆ For example,

◆ public interface SwingConstants
◆ { int NORTH = 1;
◆ int NORTH_EAST = 2;
◆ int EAST = 3;
◆ . . .
◆ }

Advanced Topic 9.2


◆ Abstract Classes

When you extend an existing class, you have the choice whether or not to redefine the methods
◆ of the superclass. Sometimes, it is desirable to force programmers to redefine a method. That
◆ happens when there is no good default for the superclass, and only the subclass programmer
◆ can know how to implement the method properly.
9.7 Interfaces 367

◆ 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

Advanced Topic 9.3


◆ Final Methods and Classes

◆ In Advanced Topic 9.2 you have seen how you can force other programmers to create sub-
◆ classes of abstract classes and override abstract methods. Occasionally, you may want to do
◆ the opposite and prevent other programmers from creating subclasses or from overriding cer-
◆ tain methods. In these situations, you use the final keyword. For example, the String class
◆ in the standard Java library has been declared as

public final class String { . . . }

◆ That means that nobody can extend the String class. The reason is twofold. The compiler
◆ can generate more efficient method calls if it knows that it doesn’t have to worry about late
◆ binding. Also, the String class is meant to be immutable—string objects can’t be modified by
◆ any of their methods. Since the Java language does not enforce this, the class designers did.
◆ Nobody can create subclasses of String; therefore, you know that all String references can
◆ be copied without the risk of mutation.
◆ You can also declare individual methods as final:

◆ public class MyApplet extends Applet
◆ { . . .
◆ public final boolean checkPassword(String password)
◆ { . . .
◆ }
◆ }

◆ This way, nobody can override the checkPassword method with another method that simply
◆ returns true.

9.8 Access Control

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.

However, it is a common error to forget the keyword private, thereby opening up


a potential security hole. For example, at the time of this writing, the Window class
in the java.awt package contained the following declaration:

public class Window extends Container


{ String warningString;
. . .
}

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.

Common Error 9.4


◆ Accidental Package Access

◆ It is very easy to forget the private modifier for instance variables.
◆ public class BankAccount
◆ { . . .
◆ double balance; // Package access really intended?
◆ }

◆ Most likely, this was just an oversight. Probably the programmer never intended to grant access
◆ to this variable to other classes in the same package. The compiler won’t complain, of course.
◆ Much later, some other programmer may take advantage of the access privilege, either out of
◆ convenience or out of evil intent. This is a serious problem, and you must get into the habit
◆ of scanning your variable declarations for missing private modifiers.

Common Error 9.5


◆ Making Inherited Methods Less Accessible

◆ If a superclass declares a method to be publicly accessible, you cannot override it to be more
◆ private. For example,
◆ public class BankAccount
◆ { public void withdraw(double amount) { . . . }
◆ . . .
◆ }

◆ public TimeDepositAccount
◆ { private void withdraw(double amount) { . . . }
◆ // Error—subclass method cannot be more private
◆ . . .
◆ }

The compiler does not allow this, because the increased privacy would be an illusion. Anyone

can still call the method through a superclass reference:

◆ BankAccount account = new TimeDepositAccount();
◆ account.withdraw(100000); // calls TimeDepositAccount.withdraw

9.9 Object: The Cosmic Superclass 371

◆ Because of late binding, the subclass method is called.


◆ These errors are usually an oversight. If you forget the public modifier, your subclass
◆ method has package access, which is more restrictive. Simply restore the public modifier,
◆ and the error will go away.

Advanced Topic 9.4


◆ Protected Access

◆ We ran into some degree of grief when trying to implement the deposit method of the
◆ CheckingAccount class. That method needed access to the balance instance variable of the
◆ superclass. Our remedy was to use the appropriate methods of the superclass to set the bal-
◆ ance.
◆ Java offers another solution to this problem. The superclass can declare an instance variable
◆ as protected:

◆ public class BankAccount
◆ { . . .
◆ protected double balance;
◆ }

◆ Protected data can be accessed by the methods of a class and all its subclasses. For example,
◆ CheckingAccount inherits from BankAccount, so its methods can access the protected in-
◆ stance variables of the BankAccount class. Furthermore, protected data can be accessed by all
◆ methods of classes in the same package.

Some programmers like the protected access feature because it seems to strike a balance

between absolute protection (making all variables private) and no protection at all (making

◆ all variables public). However, experience has shown that protected variables are subject to
◆ the same kind of problems as public variables. The designer of the superclass has no control
◆ over the authors of subclasses. Any of the subclass methods can corrupt the superclass data.
◆ Furthermore, classes with protected variables are hard to modify. Even if the author of the
◆ superclass would like to change the data implementation, the protected variables cannot be
◆ changed, because someone somewhere out there might have written a subclass whose code
◆ depends on them.
◆ It is best to leave all data private. If you want to grant access to the data only to subclass
◆ methods, consider making the accessor method protected.

9.9 Object: The Cosmic Superclass

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

BankAccount Rectangle InputStream

Checking Savings
Account Account

Figure 9

The Object Class Is the Superclass of


Every Java Class

Of course, the methods of the Object class are very general. Here are the most
useful ones:

String toString() Returns a string representation


of the object
boolean equals(Object other) Tests whether the object equals
another object
Object clone() Makes a full copy of an object

It is a good idea for you to override these in your classes.

9.9.1 Overriding the toString Method


The toString method returns a string representation for each object. It is used for
debugging. For example,
Rectangle cerealBox = new Rectangle(5, 10, 20, 30);
String s = cerealBox.toString();
// sets s to "java.awt.Rectangle[x=5,y=10,width=20,height=30]"

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

"cerealBox means " + cerealBox;


On one side of the + concatenation operator is a string, but on the other side is an
object reference. The Java compiler automatically invokes the toString method to
turn the object into a string. Then both strings are concatenated. In this case, the
result is the string
"cerealBox means java.awt.Rectangle[x=5,y=10,width=20,height=30]"
This works only if one of the objects is already a string. If you try to apply the +
operator to two objects, neither of which is a string, then the compiler reports an
error.
The compiler can invoke the toString method because it knows that every object
has a toString method: Every class extends the Object class, and that class defines
toString.
As you know, even numbers are converted to strings when they are concatenated
with other strings. For example,
int age = 18;
String s = "Harry's age is " + age;
// sets s to "Harry's age is 18"
In this case, the toString method is not involved. Numbers are not objects, and
there is no toString method for them. There is only a small set of number types,
however, and the compiler knows how to convert them to strings.
Let’s try the toString method for the BankAccount class:
BankAccount momsSavings = new BankAccount(5000);
String s = momsSavings.toString();
// sets s to something like "BankAccount@d24606bf"
That’s disappointing—all that’s printed is the name of the class, followed by the
address of the object in memory. We don’t care where the object is in memory. We
want to know what is inside the object, but, of course, the toString method of the
Object class does not know what is inside our BankAccount class. Therefore, we
have to override the method and supply our own version in the BankAccount class.
We’ll follow the same format that the toString method of the Rectangle class
uses: first print the name of the class, and then the values of the instance variables
inside brackets.
public class BankAccount
{ . . .
public String toString()
{ return "BankAccount[balance=" + balance + "]";
}
}

This works better:


BankAccount momsSavings = new BankAccount(5000);
String s = momsSavings.toString();
// sets s to "BankAccount[balance=5000]"
374 Chapter 9. Inheritance and Interfaces

9.9.2 Overriding the equals Method


The equals method is called whenever you want to compare whether two objects
have the same contents:
if (account1.equals(account2)) . . .
// contents are the same

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

9.9.3. Overriding the clone Method


You know that copying an object reference simply gives you two references to the
same object:

BankAccount account1 = new BankAccount(1000);


BankAccount account2 = account1;
account2.deposit(500);
// now both account1 and account2 have a balance of 1500

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.

public class BankAccount


{ . . .
public Object clone()
{ // make an account with the identical balance
BankAccount clonedAccount = new BankAccount(balance);
return clonedAccount;
}
}

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.

Productivity Hint 9.1


◆ Supply toString in All Classes

◆ If you have a class whose toString() method returns a string that describes the object state,
◆ then you can simply call System.out.println(x) whenever you need to inspect the cur-
◆ rent state of an object x. This works because the println method of the PrintStream
◆ class invokes x.toString() when it needs to print an object. That is extremely helpful if
◆ there is an error in your program and your objects don’t behave the way you think they
◆ should. You can simply insert a few print statements and peek inside the object state during
◆ the program run. Some debuggers can even invoke the toString method on objects that
◆ you inspect.
◆ Sure, it is a bit more trouble to write a toString method when you aren’t sure your pro-
◆ gram ever needs one—after all, it might just work correctly on first try. Then again, many
◆ programs don’t work on first try. As soon as you find out that yours doesn’t, consider adding
◆ those toString methods so that you can easily print out objects.

Common Error 9.6


◆ Forgetting to Clone

◆ In Java, object variables contain references to objects, not actual objects. This can be convenient
◆ for giving two names to the same object:

BankAccount harrysChecking = new BankAccount();

BankAccount slushFund = harrysChecking;

// Use Harry’s checking account for the slush fund

slushFund.deposit(80000)

// a lot of money ends up in Harry’s checking account

◆ However, if you don’t intend two references to refer to the same object, then this is a problem.
◆ In that case, you should use the clone method:

◆ BankAccount slushFund = (BankAccount)harrysChecking.clone();
9.9 Object: The Cosmic Superclass 377

Quality Tip 9.1


◆ Clone Mutable Instance Variables

◆ Consider the following class:
◆ public class Customer
◆ { public Customer(String aName)

{ name = aName;

account = new BankAccount();

}


◆ public String getName()
◆ { return name;
◆ }

◆ public BankAccount getAccount();
◆ { return account;
◆ }

◆ private String name;
◆ private BankAccount account;
◆ }

◆ This class looks very boring and normal, but the getAccount method has a curious property.
◆ It breaks encapsulation, because anyone can modify the object state without going through the
◆ public interface:

Customer harry = new Customer("Harry Handsome");

BankAccount account = harry.getAccount();

◆ // anyone can withdraw money!
◆ account.withdraw(100000);
◆ Maybe that wasn’t what the designers of the class had in mind? Maybe they only wanted
◆ class users to inspect the account? In such a situation, you should clone the object reference:

◆ public BankAccount getAccount();
◆ { return (BankAccount)account.clone();
◆ }

Do you also need to clone in the getName method? No—that method returns a string, and

strings are immutable. It is safe to give out a reference to an immutable object.

◆ The rule of thumb is that a class should clone all references to mutable objects that it gives
◆ out.
◆ The converse is true as well—a class should ideally clone references that it receives.
◆ public Customer(String aName, BankAccount anAccount)
◆ { name = aName;
◆ account = (BankAccount)anAccount.clone();
◆ }

◆ Otherwise, someone else can continue withdrawing from the account object that they give to
◆ the constructor.
378 Chapter 9. Inheritance and Interfaces

Advanced Topic 9.5


◆ Using Object.clone

◆ You saw how to clone an object by constructing a new object with the same state:

◆ public class BankAccount
◆ { . . .
◆ public Object clone()
◆ { // make an account with the identical balance
◆ BankAccount clonedAccount = new BankAccount(balance);
◆ return clonedAccount;
◆ }
◆ }

◆ But if you have an object with many instance variables, it can be tedious to copy them all into
◆ a new object. The Object.clone method automates that task. It simply creates a new object
◆ whose instance variables are copies of the original.
◆ However, this Object.clone method must be used with care. It only shifts the problem
◆ of cloning by one level and does not completely solve it. Specifically, if an object contains a
◆ reference to another object, then the Object.clone method makes a copy, not another clone,




◆ Customer

“Harry Handsome”

◆ name
◆ account
BankAccount







Customer

◆ name
◆ account





◆ Figure 11


◆ The Object.clone Method Makes a
◆ Shallow Copy
9.9 Object: The Cosmic Superclass 379

◆ 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

9.10.1 Organizing Related Classes into


Packages
A Java program consists of a collection of classes. So far, most of your programs
have consisted of a single class or a small number of classes that were placed into
a single file. When your programs get larger or you work in a team, that situation
changes; you will need to split your code into separate source files. There are two
primary reasons why this split-up is necessary. First, it takes time to compile a file,
and it seems silly to wait for the compiler to translate code over and over that doesn’t
change. If your code is distributed over several source files, then only those files that
you changed need to be recompiled. The second reason becomes apparent when
you work with other programmers in a team. It would be very difficult for multi-
ple programmers to edit a single source file simultaneously. Therefore, the program
9.10 Packages 381

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;

Table 1. Important Packages in the Java Library


Sample
Package Purpose classes
java.lang Language support Math
java.util Utilities Random
java.io Input and output PrintStream
java.awt Abstract Windowing Toolkit Color
java.applet Applets Applet
java.net Networking Socket
java.sql Database access through ResultSet
Structured Query Language
javax.swing Swing user interface JButton
omg.org.CORBA Common Object Request Broker Architecture ORB
for distributed objects
382 Chapter 9. Inheritance and Interfaces

import java.io.BufferedReader;
import java.io.IOException;

public class ConsoleReader


{ . . .
}
Not only must the package name be placed into the source file; the source file itself
must be placed in a special location. The next section describes where to place the file.
In addition to the named packages (such as java.util), there is a special pack-
age, called the default package, which has no name. If you did not include any
package statement at the top of your source file, its classes are placed in the default
package.

9.10.2 Importing Packages


If you want to use a class from a package, you can refer to it by its full name (pack-
age name plus class name). For example, java.awt.Color refers to the Color class
in the java.awt package, and com.horstmann.ccj.ConsoleReader refers to the
ConsoleReader class in the com.horstmann.ccj package:

java.awt.Color backgroundColor = new java.awt.Color(1.0, 0.7, 0.7);

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.

Common Error 9.7


◆ Confusing Dots

◆ In Java, the dot symbol ( . ) is used as a separator in the following situations:

◆ ◆ Between package names (java.util)
◆ ◆ Between package and class names (java.util.Random)

◆ ◆ Between class and inner class names (Ellipse2D.Double)
◆ ◆ Between class and instance variable names (Math.PI)

◆ ◆ Between objects and methods (position.move(10, -10))

◆ When you see a long chain of dot-separated names, it can be a challenge to find out which part
◆ is a package name, a class name, an instance variable name, and a method name. Consider

◆ java.lang.System.out.println(x);

◆ Since the println is followed by an opening parenthesis, it must be a method name. There-
◆ fore, out must be either an object or a class with a static println method. (Of course,
◆ we know that out is an object reference of type PrintStream.) Again, it is not at all clear,
◆ without context, whether System is another object, with a public variable out, or a class
◆ with a static variable. Judging from the number of pages that the Java reference manual
◆ [1] devotes to this issue, even the compiler has trouble interpreting these dot-separated
◆ sequences of strings.
◆ To avoid problems, it is helpful to adopt a strict coding style. If class names always start
◆ with an uppercase letter, while variable, method, and package names always start with a low-
◆ ercase letter then confusion can be avoided.
384 Chapter 9. Inheritance and Interfaces

9.10.3 Package Names


Placing related classes into a package is clearly a convenient mechanism to orga-
nize classes. However, there is a more important reason for packages: to avoid
name clashes. In a large project, it is inevitable that two people will come up with
the same name for the same concept. This even happens in the standard Java
class library (which has now grown to thousands of classes). There is a class
Object in the java.lang package and an interface, also called Object, in the
org.omg.CORBA package. Fortunately, you never needed to import both packages.
If you did, then the name Object would be ambiguous. However, thanks to the
package concept, not all is lost. You can still tell the Java compiler exactly which
Object class you need, simply by referring to them as java.lang.Object and
org.omg.CORBA.Object.
Of course, for the package-naming convention to work, there must be some way
to ensure that package names are unique. It wouldn’t be good if the car maker BMW
placed all its Java code into the package bmw, and some other programmer (perhaps
Bertha M. Walters) had the same bright idea. To avoid this problem, the inventors
of Java recommend that you use a package-naming scheme that takes advantage of
the uniqueness of Internet domain names.
If your company or organization has an Internet domain name, then you have
an identifier that is guaranteed to be unique—the organizations who assign domain
names take care of that. For example, I have a domain name horstmann.com, and
there is nobody else on the planet with the same domain name. (I was lucky that the
domain name horstmann.com had not been taken by anyone else when I applied.
Bertha M. Walters won’t be so lucky—bmw.com has already been assigned to some-
one else, namely the car company.) To get a package name, turn the domain name
around, to produce a package name prefix:
org.omg
com.horstmann

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.

9.10.4 How Classes Are Located


If the Java compiler has been properly set up on your system, and you use only the
standard classes, you ordinarily need not worry about the location of class files and
9.10 Packages 385

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:.

A typical example for Windows would be


set CLASSPATH=c:\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.

Random Fact 9.1


◆ Operating Systems

◆ Without an operating system, a computer would not be useful. Minimally, you need an op-
◆ erating system to locate files and to start programs. The programs that you run need services
◆ from the operating system to access devices and to interact with other programs. Operating
◆ systems on large computers need to provide more services than those on personal computers.
◆ Here are some typical services:

◆ ◆ Program loading. Every operating system provides some way of launching application
◆ programs. The user indicates what program should be run, usually by typing the name
◆ of the program in or by clicking on an icon. The operating system locates the program
◆ code, loads it in memory, and starts it.

◆ Managing files. A storage device such as a hard disk is, electronically, simply a device

capable of storing a huge sequence of zeroes and ones. It is up to the operating system

to bring some structure to the storage layout and organize it into files, folders, and

so on. The operating system also needs to impose some amount of security and re

◆ dundancy into the file system so that a power outage does not jeopardize the con
◆ tents of an entire hard disk. Some operating systems do a better job in this regard than
◆ others.
◆ ◆ Virtual memory. RAM is expensive, and few computers have enough RAM to hold all
◆ programs and their data that a user would like to run simultaneously. Most operating
◆ systems extend the available memory by storing some data on the hard disk. The ap-
◆ plication programs do not realize what is happening. When a program accesses a data
◆ item that is currently not in RAM, the processor senses this and notifies the operating
◆ system. The operating system swaps the needed data from the hard disk into RAM,
◆ simultaneously swapping out a memory block of equal size that had not been accessed

for some time.

◆ ◆ Handling multiple users. The operating systems of large and powerful computers allow
◆ simultaneous access by multiple users. Each user is connected to the computer through
◆ a separate terminal. The operating system authenticates users by checking that they
Chapter Summary 387

◆ 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.

3. Subclass references are automatically converted to superclass references. The op-


posite conversion requires a cast. If the reference is not actually a reference to the
desired subclass, an exception is thrown.

4. The instanceof operator tests whether an object belongs to a particular class.


388 Chapter 9. Inheritance and Interfaces

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.

7. Polymorphism and overloading are both mechanisms to select among methods


with the same name, but they have one essential difference. Overloading is resolved
at compile time and is therefore called early binding. Polymorphism is resolved at run
time and is therefore called late binding.

8. An interface is equivalent to a class with no data and no method implementations.


A class can only have one superclass, but it can implement arbitrarily many inter-
faces.You can have references whose type is an interface, but you cannot have in-
stances of an interface.

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.

Classes, Objects, and Methods


Introduced in This Chapter
java.lang.Cloneable
java.lang.Comparable
compareTo
java.lang.Object
clone
toString
java.awt.Shape
Review Exercises 389

Review Exercises

Exercise R9.1. What is the balance of b after the following operations?


SavingsAccount b = new SavingsAccount(10);
b.deposit(5000);
b.withdraw(b.getBalance() / 2);
b.addInterest();

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

Exercise R9.8. In an object-oriented traffic simulation system, we have the follow-


ing classes:
Vehicle
Car
Truck
Sedan
Coupe
PickupTruck
SportUtilityVehicle
Minivan
Bicycle
Motorcycle

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.12. (Tricky.) Consider the two calls


public class D extends B
{ public void f()
{ this.g(); // 1
}
public void g()
{ super.g(); // 2
}
. . .
}

Which of them is an example of early binding? Which of them is an example of late


binding?

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.15. Make a trivial change to the ConsoleReader.java file, by adding


a comment. Save it and note the exact time you modified it. Don’t recompile the
ConsoleReader class. Recompile any program that uses the ConsoleReader class.
Then check the file date of ConsoleReader.class file. Explain.

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.1. Enhance the addInterest method of the SavingsAccount class to


compute the interest on the minimum balance since the last call to addInterest. Hint:
You need to modify the withdraw method as well, and you need to add an instance
variable to remember the minimum balance.

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.5. Write a superclass Worker and subclasses HourlyWorker and


SalariedWorker. Every worker has a name and a salary rate. Write a method
computePay(int hours) that computes the weekly pay for every worker. An
hourly worker gets paid the hourly wage for the actual number of hours worked, if
hours is at most 40. If the hourly worker worked more than 40 hours, the excess
is paid at time and a half. The salaried worker gets paid the hourly wage for 40
hours, no matter what the actual number of hours is. Write a static method that uses
polymorphism to compute the pay of any Worker. 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

Then write a method randomVehicle that randomly generates Vehicle references,


half of them cars and half of them trucks, with random positions. Call it ten times
and draw all of them.

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.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy