Chapter 10. Inheritance

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.

Inheritance Hierarchies

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.

Note

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.

A Hierarchy of Vehicle Types

Figure 10.1. A Hierarchy of Vehicle Types

A Part of the Hierarchy of Swing User-Interface Components

Figure 10.2. A Part of the Hierarchy of Swing User-Interface Components

Inheritance Hierarchy for Bank Account Classes

Figure 10.3. Inheritance Hierarchy for Bank Account Classes

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:

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

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

Inheritance Hierarchy for Bank Account Classes

2. Why don't we place the addInterest method in the BankAccount class?

Implementing Subclasses

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
}

Note

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

Note

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.

Note

The instance variables declared in the superclass are present in subclass objects.

Layout of a Subclass Object

Figure 10.4. Layout of a Subclass 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);

Note

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.

Note

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 BankAccount
5 {
6    private double interestRate;
7
8    /**
9       Constructs a bank account with a given interest rate.
10       @param rate the interest rate
11    */
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 }
Inheritance

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?

Overriding Methods

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.

Note

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() { . . . }
}
Overriding Methods

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

Note

Use the super reserved word to call a method of the superclass.

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;
   }
   ...
}
Calling a Superclass Method

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?

Subclass Construction

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.

Note

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 BankAccount
5 {
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 balance
14    */
15    public CheckingAccount(double initialBalance)
16    {
17       // Construct superclass
18        super(initialBalance);
19
20       // Initialize transaction count
21        transactionCount = 0;
22    }
23
24    public void deposit(double amount)
25    {
26        transactionCount++;
27       // Now add amount to balance
28       super.deposit(amount);
29    }
30
31    public void withdraw(double amount)
32    {
33        transactionCount++;
34       // Now subtract amount from balance
35        super.withdraw(amount);
36    }
37
38    /**
39       Deducts the accumulated fees and resets the
40       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 }
Calling a Superclass Constructor

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?

Converting Between Subclass and Superclass Types

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

Note

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);
Variables of Different Types Can Refer to the Same Object

Figure 10.6. Variables of Different Types Can Refer to the Same Object

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

Note

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

The instanceof Operator

12. Why can't we change the second parameter of the transfer method to the type Object?

Polymorphism and Inheritance

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

Note

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 and
3    its subclasses.
4 */
5 public class AccountTester
6 {
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 month
23       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
Polymorphism and Inheritance

14. If a refers to a checking account, what is the effect of calling a.transfer(1000, a)?

Object: The Cosmic Superclass

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

Note

Every class extends the Object class either directly or indirectly.

The Object Class Is the Superclass of Every Java Class

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

Method

Purpose

String toString()

Returns a string representation of the object

boolean equals(Object otherObject)

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 methods in your classes.

Overriding the toString Method

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;

Note

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]"

Overriding the equals Method

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

Note

When implementing the equals method, test whether two objects have equal state.

Two References to Equal Objects

Figure 10.8. Two References to Equal Objects

Two References to the Same Object

Figure 10.9. Two References to the Same Object

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.

The clone Method

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

Note

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.

Cloning Objects

Figure 10.10. Cloning Objects

Cloning Objects

16. Can you implement equals in terms of toString? Should you?

Using Inheritance to Customize Frames

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.

Note

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);
   }
}
Using Inheritance to Customize Frames

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

Summary of Learning Objectives

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.

Classes, Objects, and Methods Introduced in this Chapter

java.lang.Cloneable                        java.lang.Object
java.lang.CloneNotSupportedException          clone
                                              toString

Media Resources

  • Worked Example Implementing an Employee Hierarchy for Payroll Processing

  • • Lab Exercises

  • Media Resources
  • Media Resources
  • Media Resources

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

  1. Employee, Manager

  2. Polygon, Triangle

  3. GraduateStudent, Student

  4. Person, Student

  5. Employee, GraduateStudent

  6. BankAccount, CheckingAccount

  7. Vehicle, Car

  8. Vehicle, Minivan

  9. Car, Minivan

  10. 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();
  1. x = y;

  2. y = x;

  3. y = new Sandwich();

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

  1. Rectangle r = new Rectangle(5, 10, 20, 30);

  2. if (r instanceof Rectangle) . . .

  3. if (r instanceof Point) . . .

  4. if (r instanceof Rectangle2D.Double) . . .

  5. if (r instanceof RectangularShape) . . .

  6. if (r instanceof Object) . . .

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

Programming Exercises

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:

Programming Exercises

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.

Programming Projects

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.

Answers to Self-Check Questions

  1. To express the common behavior of text fields and text components.

  2. Not all bank accounts earn interest.

  3. Two instance variables: balance and interestRate.

  4. deposit, withdraw, getBalance, and addInterest.

  5. Manager is the subclass; Employee is the superclass.

  6. The SavingsAccount class inherits the deposit, withdraw, and getBalance methods. The addInterest method is new. No methods override superclass methods.

  7. It needs to reduce the balance, and it cannot access the balance instance variable directly.

  8. So that the count can reflect the number of transactions for the following month.

  9. It was content to use the superclass constructor without parameters, which sets the balance to zero.

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

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

  12. We cannot invoke the deposit method on a variable of type Object.

  13. The object is an instance of BankAccount or one of its subclasses.

  14. The balance of a is unchanged (you withdraw from and deposit to the same account), and the transaction count is incremented twice.

  15. It certainly should—unless, of course, x is null.

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

  17. Three: InvestmentFrameViewer, InvestmentFrame, and BankAccount.

  18. The InvestmentFrame constructor adds the panel to itself.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.231.194