1.6 Inheritance

There are two fundamental mechanisms for building new classes from existing ones: inheritance and aggregation. It makes sense to inherit from an existing class Vehicle to define a class Car, since a car is a vehicle. The class Vehicle has several parts; therefore, it makes sense to define a composite object of class Vehicle that has constituent objects of such classes as Motor, Axle, and GearBox, which make up a vehicle.

Inheritance is illustrated by an example that implements a stack of characters that can print its elements on the terminal. This new stack has all the properties and behaviors of the CharStack class, but it also has the additional capability of printing its elements. Given that this printable stack is a stack of characters, it can be derived from the CharStack class. This relationship is shown in Figure 1.6. The class PrintableCharStack is called the subclass, and the class CharStack is called the superclass. The CharStack class is a generalization for all stacks of characters, whereas the class PrintableCharStack is a specialization of stacks of characters that can also print their elements.

Figure 1.6. Class Diagram Depicting Inheritance Relation

graphics/01fig06.gif

In Java, deriving a new class from an existing class requires the use of the extends clause in the subclass definition. A subclass can extend only one superclass. The subclass inherits members of the superclass. The following code fragment implements the PrintableCharStack class:

class PrintableCharStack extends CharStack {                          // (1)
    // Instance method
    public void printStackElements() {                                // (2)
        // ... implementation of the method...
    }

    // The constructor calls the constructor of the superclass explicitly.
    public PrintableCharStack(int capacity) { super(capacity); }      // (3)
}

The PrintableCharStack class extends the CharStack class at (1). Implementing the printStackElements() method in the PrintableCharStack class requires access to the field stackArray from the superclass CharStack. However, this field is private and therefore not accessible in the subclass. The subclass can access these fields if the accessibility of the fields is changed to protected in the CharStack class. Example 1.3 uses a version of the class CharStack, which has been modified accordingly. Implementation of the printStackElements() method is shown at (2). The constructor of the PrintableCharStack class at (3) calls the constructor of the superclass CharStack in order to initialize the stack properly.

Example 1.3 Defining a Subclass
// Source Filename: CharStack.java
public class CharStack {
    // Instance variables
    protected char[] stackArray;  // The array that implements the stack.
    protected int    topOfStack;  // The top of the stack.

    // The rest of the definition is the same as in Example 1.2.
}

// Source Filename: PrintableCharStack.java
public class PrintableCharStack extends CharStack {                   // (1)
    // Instance method
    public void printStackElements() {                                // (2)
        for (int i = 0; i <= topOfStack; i++)
            System.out.print(stackArray[i]); // print each char on terminal
        System.out.println();
    }
    // Constructor calls the constructor of the superclass explicitly.
    PrintableCharStack(int capacity) { super(capacity); }             // (3)
}

Objects of the PrintableCharStack class will respond just like the objects of the CharStack class, but they will also have the additional functionality defined in the subclass:

PrintableCharStack aPrintableCharStack = new PrintableCharStack(3);
aPrintableCharStack.push('H');
aPrintableCharStack.push('i');
aPrintableCharStack.push('!');
aPrintableCharStack.printStackElements();    // Prints "Hi!" on the terminal