Next: Overriding methods. Up: Subclasses. Previous: None.


Inheritance

Textbook: Section 11.1

Sometimes there are many different distinct types of a certain object. For example, a bank has many accounts, but some of them are savings accounts, and some are checking accounts. It also has many employees - some are tellers, while others are accountants, and others are vice-presidents, and others are janitors.

It's intuitive to have a SavingsAccount class and a CheckingAccount class, to represents accounts of each type. But both types of accounts have some common operations - for example, people deposit money to both, and people withdraw money from both. In general, we want to avoid duplicating code.

What we want is some way of expressing that both are Accounts, but with some additional features. To accomplish this, we might somehow define both SavingsAccount and CheckingAccount as subclasses of the Account class. This expresses that both are types of accounts, and that everything that applies to an Account should apply to a SavingsAccount also. We would say that SavingsAccount extends the Account class.

Developing a subclass

So we write the following.

public class SavingsAccount extends Account {
    private double interest;
    public SavingsAccount(double inter) {
        interest = inter;
    }
    public void addInterest() {
        deposit(getBalance() * interest);
    }
}
With this, a SavingsAccount inherits all the pieces of Account we've defined before. It's completely legitimate to write the following.
SavingsAccount mine = new SavingsAccount(0.02);
mine.deposit(1000.0); // deposit $1000 into account
mine.addInterest();
As a SavingsAccount, mine inherits the deposit() instance method from from SavingsAccount's superclass, Account. And of course, we defined the addInterest() instance method when we defined the SavingsAccount, so it gets that method also.

In memory, a SavingsAccount has two instance variables: the balance instance variable inherited from the Account class, and the interest instance variable defined directly in the SavingsAccount class. The balance variable is invisible to the SavingsAccount instance methods, since it is defined as private. But it still exists, so that the methods inherited from SavingsAccount (like deposit()) can work with it.

Writing constructors

A subclass inherits everything from its parent. Everything, that is, except the parent's constructor methods.

When I wrote the SavingsAccount, I defined a new constructor method for initializing the interest instance variable based on the constructor method parameter. What you didn't see is that the computer automatically called the default constructor method for the parent class, which would have initialized balance to 0.0.

When you create a new object (as in new SavingsAccount(0.02)), the computer actually goes through a four-step process.

  1. The computer allocates memory to hold all the instance variables associated with the new object type.

  2. If the object's class has a superclass, the computer calls the default constructor method for that superclass, to initialize any instance variables defined by the parent class.

  3. The computer calls the constructor method for the object's class, to initialize instance variables defined by the object's class. Any arguments listed in the parentheses are passed in as parameters to this constructor method.

  4. The computer returns, as the value of the new expression, the address of the allocated memory.

Ah, you say, but what if I don't want to use the default constructor of the superclass in Step 2? What if I have another superclass constructor method I want to use instead? Java has a way of indicating that: You add, as the first line of the constructor method, something that looks like a call to a super method.

public SavingsAccount(double inter, double initial) {
    super(initial);
    interest = inter;
}
In the parentheses after super is a list of the arguments that should be passed to the appropriate constructor method for the superclass. In this case, we're calling the Account constructor method that initializes the balance to a number (in this case, it's the number passed in as the second argument to the SavingsAccount constructor method).

Inheritance hierarchy

We can diagram the subclass relationship as a tree, like the following.

            Account
            /     \
SavingsAccount   CheckingAccount
This illustrates that both SavingsAccount and CheckingAccount are subclasses of the Account class.

For large programs, the relationship among classes creates quite a complex tree. You can (and do) have some subclasses of subclasses, or even subclasses of subclasses of subclasses.

                Person
                /    \
          Employee  Customer
         /       \
WagedEmployee     SalariedEmployee
   /       \        /        \
Janitor Teller Accountant VicePresident
This whole tree is called an inheritance hierarchy.

Next: Overriding methods. Up: Subclasses. Previous: None.