CHAPTER GOALS
To learn about inheritance
To understand how to inherit and override superclass methods
To be able to invoke superclass constructors
To learn about protected and package access control
To understand the common superclass Object
and how to override its toString
and equals
methods
G To use inheritance for customizing user interfaces
In this chapter, we discuss the important concept of inheritance. Specialized classes can be created that inherit behavior from more general classes. You will learn how to implement inheritance in Java, and how to make use of the Object
class—the most general class in the inheritance hierarchy.
In the real world, you often categorize concepts into hierarchies. Hierarchies are frequently represented as trees, with the most general concepts at the root of the hierarchy and more specialized ones towards the branches. Figure 1 shows a typical example.
In Java it is equally common to group classes in inheritance hierarchies. The classes representing the most general concepts are near the root, more specialized classes towards the branches. For example, Figure 2 shows part of the hierarchy of Swing user-interface components in Java.
Sets of classes can form complex inheritance hierarchies.
We must introduce some more terminology for expressing the relationship between the classes in an inheritance hierarchy. The more general class is called the superclass. The more specialized class that inherits from the superclass is called the subclass. In our example, JPanel
is a subclass of JComponent
.
Figure 2 uses the UML notation for inheritance. In a class diagram, you denote inheritance by a solid arrow with a "hollow triangle" tip that points to the super-class.
When designing a hierarchy of classes, you ask yourself which features and behaviors are common to all the classes that you are designing. Those common properties are placed in a superclass. For example, all user-interface components have a width and height, and the getWidth
and getHeight
methods of the JComponent
class return the component's dimensions. More specialized properties can be found in subclasses. For example, buttons can have text and icon labels. The class Abstract-Button
, but not the superclass JComponent
, has methods to set and get the button text and icon, and instance variables to store them. The individual button classes (such as JButton, JRadioButton
, and JCheckBox
) inherit these properties. In fact, the Abstract-Button
class was created to express the commonality among these buttons.
We will use a simpler example of a hierarchy in our study of inheritance concepts. Consider a bank that offers its customers the following account types:
The checking account has no interest, gives you a small number of free transactions per month, and charges a transaction fee for each additional transaction.
The savings account earns interest that compounds 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 P10.1 asks you to implement this enhancement.)
Figure 3 shows the inheritance hierarchy. Exercise P10.2 asks you to add another class to this hierarchy.
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.
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 overridden to count the transactions.
The savings account needs a method addInterest
to add interest.
To summarize: The subclasses support all methods from the superclass, but their implementations may be modified to match the specialized purposes of the subclasses. In addition, subclasses are free to introduce additional methods.
2. Why don't we place the addInterest
method in the BankAccount
class?
In this section, we begin building the inheritance hierarchy of bank account classes. You will learn how to form a subclass from a given superclass. Let's start with the SavingsAccount
class. Here is the syntax for the class declaration:
public class SavingsAccount extends BankAccount {added instance variables
new methods
}
Inheritance is a mechanism for extending existing classes by adding instance variables and methods.
In the SavingsAccount
class declaration you specify only new methods and instance variables. The SavingsAccount
class automatically inheritsthe methods of the BankAc-count
class. For example, the deposit
method automatically applies to savings accounts:
SavingsAccount collegeFund = new SavingsAccount(10); // Savings account with 10% interest collegeFund.deposit(500); // OK to use BankAccount method with SavingsAccount object
A subclass inherits the methods of its superclass.
Let's see how savings account objects are different from BankAccount
objects. We will set an interest rate in the constructor, and 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
. The new method and instance variable must be declared in the subclass.
public class SavingsAccount extends BankAccount { private double interestRate; public SavingsAccount(double rate) { Constructor implementation } public void addInterest() { Method implementation } }
A subclass object automatically has the instance variables declared in the superclass. For example, a SavingsAccount
object has an instance variable balance
that was declared in the BankAccount
class.
Any new instance variables that you declare in the subclass are present only in subclass objects. For example, every SavingsAccount
object has an instance variable interestRate
. Figure 4 shows the layout of a SavingsAccount
object.
Next, you need to implement the new addInterest
method. The method computes the interest due on the current balance and deposits that interest to the account.
public class SavingsAccount extends BankAccount { private double interestRate; public SavingsAccount(double rate) { interestRate = rate; } public void addInterest() { double interest = getBalance() * interestRate / 100; deposit(interest); } }
The addInterest
method calls the getBalance
and deposit
methods rather than directly updating the balance
variable of the superclass. This is a consequence of encapsulation. The balance
variable was declared as private
in the BankAccount
class. The addInterest
method is declared in the SavingsAccount
class. It does not have the right to access a private instance variable of another class.
Note how the addInterest
method calls the inherited getBalance
and deposit
methods without specifying an implicit parameter. This means that the calls apply to the implicit parameter of the addInterest
method.
In other words, the statements in the addInterest
method are a shorthand for the following statements:
double interest = this.getBalance() * this.interestRate / 100; this.deposit(interest);
A subclass has no access to private instance variables of its superclass.
This completes the implementation of the SavingsAccount
class. You will find the complete source code below.
You may wonder at this point in what way inheritance differs from implementing an interface. An interface is not a class. It has no behavior. It merely tells you which methods you should implement. A superclass has behavior that the subclasses inherit.
Inheriting from a class differs from implementing an interface: The subclass inherits behavior from the superclass.
ch10/accounts/SavingsAccount.java
1
/**2
An account that earns interest at a fixed rate.3
*/4
public class SavingsAccount extends BankAccount5
{6
private double interestRate;7
8
/**9
Constructs a bank account with a given interest rate.10
@param rate the interest rate11
*/12
public SavingsAccount(double rate)13
{14
interestRate = rate;15
}16
17
/**18
Adds the earned interest to the account balance.19
*/20
public void addInterest()21
{22
double interest = getBalance() * interestRate / 100;23
deposit(interest);24
}25
}
4. Name four methods that you can apply to SavingsAccount
objects.
5. If the class Manager
extends the class Employee
, which class is the superclass and which is the subclass?
A subclass method overrides a superclass method if it has the same name and parameter types as a superclass method. When such a method is applied to a subclass object, the overriding method, and not the original method, is executed.
A subclass can inherit a superclass method or override it by providing another implementation.
We turn to the CheckingAccount
class for an example of overriding methods. Recall that the BankAccount
class has three methods:
public class BankAccount { ... public void deposit(double amount) { ... } public void withdraw(double amount) { ... } public double getBalance() { ... } }
The CheckingAccount
class declares these methods:
public class CheckingAccount extends BankAccount { . . . public void deposit(double amount) { . . . } public void withdraw(double amount) { . . . } public void deductFees() { . . . } }
The deposit
and withdraw
methods of the CheckingAccount
class override the deposit
and withdraw
methods of the BankAccount
class to handle transaction fees. However, the deductFees
method does not override another method, and the getBalance
method is not overridden.
Let's implement the deposit
method of the CheckingAccount
class. It 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 instance variable is private to the superclass BankAccount
. Subclass methods have no more access rights to the private data of the superclass than any other methods. If you want to modify a private superclass instance variable, you must use a public method of the superclass.
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 that purpose—namely, the deposit
method of the BankAccount
class. So we must invoke the deposit
method 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 Checking-Account
class. To invoke another method on the implicit parameter, you don't specify the parameter but simply write the method name, like this:
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 (discussed in Chapter 13).
Instead, we must be specific that we want to invoke only the superclass's deposit
method. There is a special reserved word 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 checking account, update the transaction count and call the deposit
method of the superclass.
The remaining methods of the CheckingAccount
class also invoke a superclass method.
public class CheckingAccount extends BankAccount { private static final int FREE_TRANSACTIONS = 3; private static final double TRANSACTION_FEE = 2.0; private int transactionCount; ... 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; } ... }
7. Why does the withdraw
method of the CheckingAccount
class call super.withdraw
?
8. Why does the deductFees
method set the transaction count to zero?
In this section, we discuss the implementation of constructors in subclasses. As an example, let's declare a constructor to set the initial balance of a checking account.
We want to invoke the BankAccount
constructor to set the balance to the initial balance. There is a special instruction to call the superclass constructor from a subclass constructor. You use the reserved word 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 reserved word super
is immediately followed by a parenthesis, it indicates a call to the superclass 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.
To call the superclass constructor, you use the super
reserved word in the first statement of the subclass constructor.
The dual use of the super
reserved word is analogous to the dual use of the this
reserved word (see Special Topic 3.1).
If a subclass constructor does not call the superclass constructor, the superclass must have a constructor without parameters. That constructor is used to initialize the superclass data. However, if all constructors of the superclass require parameters, 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 BankAc-count()
constructor, which sets the balance to zero. Of course, then the CheckingAc-count
constructor must explicitly deposit the initial balance.
Most commonly, however, subclass constructors have some parameters that they pass on to the superclass and others that they use to initialize subclass instance variables.
ch10/accounts/CheckingAccount.java
1
/**2
A checking account that charges transaction fees.3
*/4
public class CheckingAccount extends BankAccount5
{6
private static final int FREE_TRANSACTIONS = 3;7
private static final double TRANSACTION_FEE = 2.0;8
9
private int transactionCount;10
11
/**12
Constructs a checking account with a given balance.13
@param initialBalance the initial balance14
*/15
public CheckingAccount(double initialBalance)16
{17
// Construct superclass18
super(initialBalance);19
20
// Initialize transaction count21
transactionCount = 0;22
}23
24
public void deposit(double amount)25
{26
transactionCount++;27
// Now add amount to balance28
super.deposit(amount);29
}30
31
public void withdraw(double amount)32
{33
transactionCount++;34
// Now subtract amount from balance35
super.withdraw(amount);36
}37
38
/**39
Deducts the accumulated fees and resets the40
transaction count.41
*/42
public void deductFees()43
{44
if (transactionCount > FREE_TRANSACTIONS)45
{46
double fees = TRANSACTION_FEE *47
(transactionCount - FREE_TRANSACTIONS);48
super.withdraw(fees);49
}50
transactionCount = 0;51
}52
}
10. When you invoke a superclass method with the super
reserved word, does the call have to be the first statement of the subclass method?
It is often necessary to convert a subclass type to a superclass type. Occasionally, you need to carry out the conversion in the opposite direction. This section discusses the conversion rules.
The class SavingsAccount
extends the class BankAccount
. In other words, a Savings-Account
object is a special case of a BankAccount
object. Therefore, a reference to a SavingsAccount
object can be converted to a BankAccount
reference.
SavingsAccount collegeFund = new SavingsAccount(10); BankAccount anAccount = collegeFund; // OK
Subclass references can be converted to superclass references.
Furthermore, all references can be converted to the type Object
.
Object anObject = collegeFund; // OK
Now the three object references stored in collegeFund, anAccount
, and anObject
all refer to the same object of type SavingsAccount
(see Figure 6).
However, the variables anAccount
and anObject
know less than the full story about the object references that they store. Because anAccount
is a variable of type BankAc-count
, you can invoke the deposit
and withdraw
methods. You cannot use the addInterest
method, though—it is not a method of the BankAccount
class:
anAccount.deposit(1000); // OK anAccount.addInterest(); // No—not a method of the type of the anAccount variable
And, of course, the variable anObject
knows even less. You can't even invoke the deposit
method on it—deposit
is not a method of the Object
class.
Why would anyone want to know less about an object reference and use a variable whose type is 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 a transfer
method that transfers money from one account to another:
public void transfer(double amount, BankAccount other) { withdraw(amount); other.deposit(amount); }
You can use this method to transfer money from one bank account to another:
BankAccount momsAccount = ... ; BankAccount harrysAccount = ... ; momsAccount.transfer(1000, harrysAccount);
You can also use the method to transfer money into a CheckingAccount
:
CheckingAccount harrysChecking = ... ; momsAccount.transfer(1000, harrysChecking); // OK to pass a CheckingAccount reference to a method expecting a BankAccount
The transfer
method expects a reference to a BankAccount
, and it gets a reference to a CheckingAccount
object. That is perfectly legal. The transfer
method doesn't actually know that, in this case, the parameter variable other
contains a reference to a Check-ingAccount
object. All it cares about is that the object can carry out the deposit
method. This is assured because the other
variable has the type BankAccount
.
Very occasionally, you need to carry out the opposite conversion, from a super-class type to a subclass type. For example, you may have a variable of type Object
, and you know that it actually holds a BankAccount
reference. In that case, you can use a cast to convert the type:
BankAccount anAccount = (BankAccount) anObject;
However, this cast is somewhat dangerous. If you are wrong, and anObject
actually refers to an object of an unrelated type, then an exception is thrown.
To protect against bad casts, you can use the instanceof
operator. It tests whether an object belongs to a particular type. For example,
anObject instanceof BankAccount
returns true
if the type of anObject
is convertible to BankAccount
. This happens if anObject
refers to an actual BankAccount
or a subclass such as SavingsAccount
. Using the
if (anObject instanceof BankAccount) { BankAccount anAccount = (BankAccount) anObject; ... }
The instanceof
operator tests whether an object belongs to a particular type.
12. Why can't we change the second parameter of the transfer
method to the type Object
?
In Java, the type of a variable does not determine the type of the object to which it refers. For example, a variable of type BankAccount
can hold a reference to an actual BankAccount
object or a subclass object such as SavingsAccount
. You already encountered this phenomenon in Chapter 9 with variables whose type was an interface. A variable whose type is Measurable
holds a reference to an object of a class that implements the Measurable
interface, perhaps a Coin
object or an object of an entirely different class.
What happens when you invoke a method on a variable of type BankAccount
? For example,
BankAccount anAccount = new CheckingAccount(); anAccount.deposit(1000);
Which deposit method is called? The anAccount
variable has type BankAccount
, so it would appear as if BankAccount.deposit
is called. On the other hand, the CheckingAccount
class provides its own deposit
method that updates the transaction count. The reference stored in the anAccount
variable actually refers to an object of the subclass CheckingAccount
, so it would be appropriate if the CheckingAccount.deposit
method were called instead.
Java uses dynamic method lookup to determine which method to invoke. The method to be called is always determined by the type of the actual object, not the type of the variable. 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
.
Have another look at the transfer
method:
public void transfer(double amount, BankAccount other) { withdraw(amount); other.deposit(amount); }
When the virtual machine calls an instance method, it locates the method of the implicit parameter's class. This is called dynamic method lookup.
Suppose you call
anAccount.transfer(1000, anotherAccount);
Two method calls are the result:
anAccount.withdraw(1000); anotherAccount.deposit(1000);
Depending on the actual types of the objects whose references are stored in anAc-count and anotherAccount
, different versions of the withdraw
and deposit
methods are called. This is an example of polymorphism. As we discussed in Chapter 9, polymorphism is the ability to treat objects with differences in behavior in a uniform way.
If you look into the implementation of the transfer
method, it may not be immediately obvious that the first method call
withdraw(amount);
depends on the type of an object. However, that call is a shortcut for
this.withdraw(amount);
The this
parameter holds a reference to the implicit parameter, which can refer to a BankAccount
or a subclass object.
The following program calls the polymorphic withdraw
and deposit
methods. You should manually calculate what the program should print for each account balance, and confirm that the correct methods have in fact been called.
ch10/accounts/AccountTester.java
1
/**2
This program tests the BankAccount class and3
its subclasses.4
*/5
public class AccountTester6
{7
public static void main(String[] args)8
{9
SavingsAccount momsSavings = new SavingsAccount(0.5);10
11
CheckingAccount harrysChecking = new CheckingAccount(100);12
13
momsSavings.deposit(10000);14
15
momsSavings.transfer(2000, harrysChecking);16
harrysChecking.withdraw(1500);17
harrysChecking.withdraw(80);18
19
momsSavings.transfer(1000, harrysChecking);20
harrysChecking.withdraw(400);21
22
// Simulate end of month23
momsSavings.addInterest();24
harrysChecking.deductFees();25
26
System.out.println("Mom's savings balance: "27
+ momsSavings.getBalance());28
System.out.println("Expected: 7035");29
30
System.out.println("Harry's checking balance: "31
+ harrysChecking.getBalance());32
System.out.println("Expected: 1116");33
}34
}
Program Run
Mom's savings balance: 7035.0 Expected: 7035 Harry's checking balance: 1116.0 Expected: 1116
14. If a
refers to a checking account, what is the effect of calling a.transfer(1000, a)
?
In Java, every class that is declared without an explicit extends
clause automatically extends the class Object
. That is, the class Object
is the direct or indirect superclass of every class in Java (see Figure 7).
Of course, the methods of the Object
class are very general. Here are the most useful ones:
Method | Purpose |
---|---|
| Returns a string representation of the object |
| Tests whether the object equals another object |
| Makes a full copy of an object |
It is a good idea for you to override these methods in your classes.
The toString
method returns a string representation for each object. It is useful for debugging. For example,
Rectangle box = new Rectangle(5, 10, 20, 30); String = box.toString(); state. // 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
"box=" + box;
In your classes, provide toString
methods that describe each object's state.
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
"box=java.awt.Rectangle[x=5,y=10,width=20,height=30]"
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 provides a toString
method.
As you know, numbers are also 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 primitive 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 hash code, a seemingly random code. The hash code can be used to tell objects apart—different objects are likely to have different hash codes. (See Chapter 16 for the details.)
We don't care about the hash code. 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 the 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]"
The equals
method is called whenever you want to compare whether two objects have the same contents:
if (coin1.equals(coin2)) ... // Contents are the same--see Figure 8
This is different from the test with the ==
operator, which tests whether the two references are to the same object:
if (coin1 == coin2) ... // Objects are the same--see Figure 9
When implementing the equals
method, test whether two objects have equal state.
Let us implement the equals
method for the Coin
class. You need to override the equals
method of the Object
class:
public class Coin { ... public boolean equals(Object otherObject) { ... } ... }
Now you have a slight problem. The Object
class knows nothing about coins, so it declares the otherObject
parameter of the equals
method to have the type Object
. When overriding the method, you are not allowed to change the parameter type. To overcome this problem, cast the parameter to the class Coin
:
Coin other = (Coin) otherObject;
Then you can compare the two coins.
public boolean equals(Object otherObject) { Coin other = (Coin) otherObject; return name.equals(other.name) && value == other.value; }
Note that you must use equals
to compare object references, but use ==
to compare numbers.
When you override the equals
method, you should also override the hashCode
method so that equal objects have the same hash code—see Chapter 16 for details.
You know that copying an object reference simply gives you two references to the same object:
BankAccount account = new BankAccount(1000); BankAccount account2 = account; account2.deposit(500); // Now both account and account2 refer to a bank account with 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 an identical state to the existing object (see Figure 10).
The clone
method makes a new object with the same state as an existing object.
Implementing the clone
method is quite a bit more difficult than implementing the toString
or equals
methods—see Special Topic 10.6 on page 452 for details.
Let us suppose that someone has implemented the clone
method for the Bank-Account
class. Here is how to call it:
BankAccount clonedAccount = (BankAccount) account.clone();
The return type of the clone
method is the class Object
. When you call the method, you must use a cast to convince the compiler that account.clone()
really has the same type as clonedAccount
.
16. Can you implement equals
in terms of toString
? Should you?
As you add more user-interface components to a frame, the frame can get quite complex. Your programs will become easier to understand when you use inheritance for complex frames.
Provide a JFrame
subclass for a complex frame.
To do so, design a subclass of JFrame
. Store the components as instance variables. Initialize them in the constructor of your subclass. If the initialization code gets complex, simply add some helper methods.
Here, we carry out this process for the investment viewer program in Chapter 9.
public class InvestmentFrame extends JFrame { private JButton button; private JLabel label; private JPanel panel; private BankAccount account; public InvestmentFrame() { account = new BankAccount(INITIAL_BALANCE); // Use instance variables for components label = new JLabel("balance: " + account.getBalance()); // Use helper methods createButton(); createPanel(); setSize(FRAME_WIDTH, FRAME_HEIGHT); } private void createButton() {
button = new JButton("Add Interest"); ActionListener listener = new AddInterestListener(); button.addActionListener(listener); } private void createPanel() { panel = new JPanel(); panel.add(button); panel.add(label); add(panel); } ... }
This approach differs from the programs in Chapter 9. In those programs, we simply configured the frame in the main
method of a viewer class.
It is a bit more work to provide a separate class for the frame. However, the frame class makes it easier to organize the code that constructs the user-interface elements.
Of course, we still need a class with a main
method:
public class InvestmentViewer2 { public static void main(String[] args) { JFrame frame = new InvestmentFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
18. Why does the InvestmentFrame
constructor call setSize(FRAME_WIDTH, FRAME_HEIGHT)
, whereas the main
method of the investment viewer class in Chapter 9 called frame.setSize(FRAME_WIDTH, FRAME_HEIGHT)
?
Explain the notions of inheritance, superclasses, and subclasses.
Sets of classes can form complex inheritance hierarchies.
Implement subclasses in Java.
Inheritance is a mechanism for extending existing classes by adding instance variables and methods.
A subclass inherits the methods of its superclass.
The instance variables declared in the superclass are present in subclass objects.
A subclass has no access to private instance variables of its superclass.
The more general class is called a superclass. The more specialized class that inherits from the superclass is called the subclass.
Inheriting from a class differs from implementing an interface: The subclass inherits behavior from the superclass.
Describe how a subclass can override methods from its superclass.
A subclass can inherit a superclass method or override it by providing another implementation.
Use the super
reserved word to call a method of the superclass.
Describe how a subclass can construct its superclass.
To call the superclass constructor, you use the super
reserved word in the first statement of the subclass constructor.
Describe how to convert between class and superclass types.
Subclass references can be converted to superclass references.
The instanceof
operator tests whether an object belongs to a particular type.
Describe dynamic method lookup and polymorphism.
When the virtual machine calls an instance method, it locates the method of the implicit parameter's class. This is called dynamic method lookup.
An abstract method is a method whose implementation is not specified.
An abstract class is a class that cannot be instantiated.
Protected features can be accessed by all subclasses and by all classes in the same package.
Provide appropriate overrides of the methods of the Object superclass.
Every class extends the Object
class either directly or indirectly.
In your classes, provide toString
methods that describe each object's state.
When implementing the equals
method, test whether two objects have equal state.
The clone
method makes a new object with the same state as an existing object.
Use inheritance to customize frames.
Provide a JFrame
subclass for a complex frame.
java.lang.Cloneable
java.lang.Object
java.lang.CloneNotSupportedException clone
toString
• Worked Example Implementing an Employee Hierarchy for Payroll Processing
• Lab Exercises
R10.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();
R10.2 Describe all constructors of the SavingsAccount
class. List all methods that are inherited from the BankAccount
class. List all methods that are added to the SavingsAccount
class.
R10.3 Can you convert a superclass reference into a subclass reference? A subclass reference into a superclass reference? If so, give examples. If not, explain why not.
R10.4 Identify the superclass and the subclass in each of the following pairs of classes.
Employee, Manager
Polygon, Triangle
GraduateStudent, Student
Person, Student
Employee, GraduateStudent
BankAccount, CheckingAccount
Vehicle, Car
Vehicle, Minivan
Car, Minivan
Truck, Vehicle
R10.5 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();
R10.6 Draw an inheritance diagram that shows the inheritance relationships between the classes:
Person
Employee
Student
Instructor
Classroom
Object
R10.7 In an object-oriented traffic simulation system, we have the following classes:
Vehicle
Car
Truck
Sedan
Coupe
PickupTruck
SportUtilityVehicle
Minivan
Bicycle
Motorcycle
Draw an inheritance diagram that shows the relationships between these classes.
R10.8 What inheritance relationships would you establish among the following classes?
Student
Professor
TeachingAssistant
Employee
Secretary
DepartmentChair
Janitor
SeminarSpeaker
Person
Course
Seminar
Lecture
ComputerLab
R10.9 Which of these conditions returns true?
Check the Java documentation 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) . . .
R10.10 Explain the two meanings of the super
reserved word. Explain the two meanings of the this
reserved word. How are they related?
R10.11 (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 polymorphism?
R10.12 Consider this program:
public class AccountPrinter { public static void main(String[] args) { SavingsAccount momsSavings = new SavingsAccount(0.5); CheckingAccount harrysChecking = new CheckingAccount(0); ... endOfMonth(momsSavings); endOfMonth(harrysChecking); printBalance(momsSavings); printBalance(harrysChecking); } public static void endOfMonth(SavingsAccount savings) { savings.addInterest(); } public static void endOfMonth(CheckingAccount checking) { checking.deductFees(); }
public static void printBalance(BankAccount account) { System.out.println("The balance is $" + account.getBalance()); } }
Do the calls to the endOfMonth
methods use dynamic method invocation? Inside the printBalance
method, does the call to getBalance
use dynamic method invocation?
R10.13 Explain the terms shallow copy and deep copy.
R10.14 What access attribute should instance variables have? What access attribute should static variables have? How about static final variables?
R10.15 What access attribute should instance methods have? Does the same hold for static methods?
R10.16 The static variables System.in
and System.out
are public. Is it possible to overwrite them? If so, how?
R10.17 Why are public instance variables dangerous? Are public static variables more dangerous than public instance variables?
P10.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.
P10.2 Add a TimeDepositAccount
class to the bank account hierarchy. 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 $20 penalty for early withdrawal. Construct the account with the interest rate and the number of months to maturity. In the addInterest
method, decrement the count of months. If the count is positive during a withdrawal, charge the withdrawal penalty.
P10.3 Add a class NumericQuestion
to the question hierarchy of How To 10.1. If the response and the expected answer differ by no more than 0.01, then accept it as correct.
P10.4 Add a class FillInQuestion
to the question hierarchy of How To 10.1. An object of this class is constructed with a string that contains the answer, surrounded by _ _
, for example, "The inventor of Java was _James Gosling_"
. The question should be displayed as
The inventor of Java was __s_
P10.5 Modify the checkAnswer
method of the Question
class of How To 10.1 so that it does not take into account different spaces or upper/lowercase characters. For example, the response " JAMES gosling"
should match an answer of "James Gosling"
.
P10.6 Add a class MultiChoiceQuestion
to the question hierarchy of How To 10.1 that allows multiple correct choices. The respondent should provide all correct choices, separated by spaces. Provide instructions in the question text.
P10.7 Add a class AnyCorrectChoiceQuestion
to the question hierarchy of How To 10.1 that allows multiple correct choices. The respondent should provide any one of the correct choices. The answer string should contain all of the correct choices, separated by spaces.
P10.8 Add a method addText
to the Question
class of How To 10.1 and provide a different implementation of ChoiceQuestion
that calls addText
rather than storing an array list of choices.
P10.9 Provide toString
and equals
methods for the Question
and ChoiceQuestion
classes of How To 10.1.
P10.10 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.
P10.11 Implement a superclass Person
. Make two classes, Student
and Instructor
, that inherit from Person
. A person has a name and a year of birth. A student has a major, and an instructor has a salary. Write the class declarations, the constructors, and the methods toString
for all classes. Supply a test program that tests these classes and methods.
P10.12 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, department, and salary. Make a class Executive
inherit from Manager
. Supply appropriate toString
methods for all classes. Supply a test program that tests these classes and methods.
P10.13 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 transactions and then calls endOfMonth
. Test it with instances of all concrete account classes.
P10.14 Implement an abstract class Vehicle
and concrete subclasses Car
and Truck
. A vehicle has a position on the screen. Write methods draw
that draw cars and trucks as follows:
Then write a method randomVehicle
that randomly generates Vehicle
references, with an equal probability for constructing cars and trucks, with random positions. Call it 10 times and draw all of them.
P10.15 Write a program that prompts the user for an integer, using a JOptionPane
, and then draws as many rectangles at random positions in a component as the user requested. Use inheritance for your frame class.
P10.16 Write a program that asks the user to enter an integer n into a JOptionPane
, and then draws an n-by-n grid. Use inheritance for the frame class.
Project 10.1 Your task is to program robots with varying behaviors. The robots try to escape a maze, such as the following:
* ******* * * * * ***** * * * * * * * *** * * * * *** * * * * * * ******* *
A robot has a position and a method void move(Maze m)
that modifies the position. Provide a common superclass Robot
whose move
method does nothing. Provide subclasses RandomRobot, RightHandRuleRobot
, and MemoryRobot
. Each of these robots has a different strategy for escaping. The RandomRobot
simply makes random moves. The RightHandRuleRobot
moves around the maze so that it's right hand always touches a wall. The MemoryRobot
remembers all positions that it has previously occupied and never goes back to a position that it knows to be a dead end.
Project 10.2 Implement the toString, equals
, and clone
methods for all subclasses of the BankAc-count
class, as well as the Bank
class of Chapter 7. Write unit tests that verify that your methods work correctly. Be sure to test a Bank
that holds objects from a mixture of account classes.
To express the common behavior of text fields and text components.
Not all bank accounts earn interest.
Two instance variables: balance
and interestRate
.
deposit, withdraw, getBalance
, and addInterest
.
Manager
is the subclass; Employee
is the superclass.
The SavingsAccount
class inherits the deposit, withdraw
, and getBalance
methods. The addInterest
method is new. No methods override superclass methods.
It needs to reduce the balance, and it cannot access the balance
instance variable directly.
So that the count can reflect the number of transactions for the following month.
It was content to use the superclass constructor without parameters, which sets the balance to zero.
No—this is a requirement only for constructors. For example, the Checking-Account.deposit
method first increments the transaction count, then calls the super-class method.
We want to use the method for all kinds of bank accounts. Had we used a parameter of type SavingsAccount
, we couldn't have called the method with a CheckingAccount
object.
We cannot invoke the deposit
method on a variable of type Object
.
The object is an instance of BankAccount
or one of its subclasses.
The balance of a
is unchanged (you withdraw from and deposit to the same account), and the transaction count is incremented twice.
It certainly should—unless, of course, x
is null
.
If toString
returns a string that describes all instance variables, you can simply call toString
on the implicit and explicit parameters, and compare the results. However, comparing the instance variables is more efficient than converting them into strings.
Three: InvestmentFrameViewer, InvestmentFrame
, and BankAccount
.
The InvestmentFrame
constructor adds the panel to itself.
3.137.220.92