Implementing Inheritance – Object-Oriented Programming

Implementing Inheritance – Object-Oriented Programming

5.1 Implementing Inheritance

Inheritance is one of the fundamental mechanisms for code reuse in OOP. It allows new classes to be derived from existing ones. The new class (also called a subclass, subtype, derived class, or child class) can inherit members from the old class (also called a superclass, supertype, base class, or parent class). The subclass can add new behavior and properties and, under certain circumstances, modify its inherited behavior.

A subclass specifies the name of its superclass in the subclass header using the extends clause.

Click here to view code image

class TubeLight extends Light { … }   // TubeLight is a subclass of Light.

The subclass specifies only the additional new and modified members in its class body. The rest of its declaration is made up of its inherited members. If no extends clause is specified in the header of a class declaration, the class implicitly inherits from the java.lang.Object class. Selected methods inherited by all objects from the Object class are covered in connection with object comparison (Chapter 14, p. 741) and thread synchronization (§22.4, p. 1396).

Inheritance of members is closely tied to their declared accessibility. If a superclass member is accessible by its simple name in the subclass (without the use of any extra syntax, like super), that member is considered inherited. Conversely, private, overridden, and hidden members of the superclass are not inherited. Inheritance should not be confused with the existence of such members in the state of a subclass object.

The declaration of the Light class at (1) in Example 5.1 implicitly extends the java.lang.Object class, as no explicit extends clause is specified. Also in Example 5.1, the subclass TubeLight at (2) explicitly uses the extends clause and specifies only members other than those that it already inherits from the superclass Light (which, in turn, inherits from the Object class). Members of the superclass Light, which are accessible by their simple names in the subclass TubeLight, are inherited by the subclass, as evident from the output in Example 5.1.

Members of the superclass that have private access are not inherited by the subclass and can only be accessed indirectly. The private field indicator of the superclass Light is not inherited, but exists in the subclass object and is indirectly accessible through public methods.

Using appropriate access modifiers, the superclass can limit which members can be accessed directly, and therefore, inherited by its subclasses. As shown in Example 5.1, the subclass can use the inherited members as if they were declared in its own class body. This is not the case for members that are declared as private in the superclass, as shown at (3), (4), and (5). Members that have package accessibility in the superclass are also not inherited by subclasses in other packages, as these members are accessible by their simple names only in subclasses within the same package as the superclass.

Since constructors are not members of a class, they are not inherited by a subclass.

Example 5.1 Extending Classes: Inheritance and Accessibility

Click here to view code image

// File: Utility.java
class Light {                           // (1)
  // Instance fields:
            int     noOfWatts;          // Wattage
  private   boolean indicator;          // On or off
  protected String  location;           // Placement
  // Static field:
  private static int counter;           // Number of Light objects created
  // Non-zero argument constructor:
  Light(int noOfWatts, boolean indicator, String location) {
    this.noOfWatts = noOfWatts;
    this.indicator = indicator;
    this.location  = location;
    ++counter;                          // Increment counter.
  }
  // Instance methods:
  public  void    switchOn()  { indicator = true; }
  public  void    switchOff() { indicator = false; }
  public  boolean isOn()      { return indicator; }
  private String  getLocation() { return location; }
  // Static methods:
  public static int getCount() { return counter; }
}
//______________________________________________________________________________
class TubeLight extends Light {         // (2) Subclass uses the extends clause.
  // Instance fields:
  private int tubeLength;               // Length in millimeters
  private int tubeDiameter;             // Diameter in millimeters
  // Non-zero argument constructor
  TubeLight(int noOfWatts, boolean indicator, String location,
            int tubeLength, int tubeDiameter) {
    super(noOfWatts, indicator, location);  // Calling constructor in superclass.
    this.tubeLength = tubeLength;
    this.tubeDiameter = tubeDiameter;
  }
  // Instance methods:
  public int getTubeLength() { return tubeLength; }

  public void printInfo() {
    System.out.println(“From the subclass:”);
    System.out.println(“Tube length (mm): ”  + getTubeLength());
    System.out.println(“Tube diameter (mm): ” + tubeDiameter);
    System.out.println();
    System.out.println(“From the superclass:”);
    System.out.println(“Wattage: ”      + noOfWatts);     // Inherited.
//  System.out.println(“Indicator: ”    + indicator);     // (3) Not inherited.
    System.out.println(“Location: ”     + location);      // Inherited.
//  System.out.println(“Counter: ”   + counter);          // (4) Not inherited.
    switchOn();                                           // Inherited
    switchOff();                                          // Inherited
    System.out.println(“Indicator: ”    + isOn());        // Inherited.
//  System.out.println(“Location: ” + getLocation());     // (5) Not inherited.
    System.out.println(“Number of lights: ” + getCount());// Inherited.
  }
}
//______________________________________________________________________________
public class Utility {
  public static void main(String[] args) {
    TubeLight loftLight = new TubeLight(18, true, “Loft”, 590, 26);
    loftLight.printInfo();
  }
}

Output from the program:

Click here to view code image

From the subclass:
Tube length (mm): 590
Tube diameter (mm): 26
From the superclass:
Wattage: 18
Location: Loft
Indicator: false
Number of lights: 1

In Java, a class can extend only one class; that is, it can have only one direct superclass. This kind of inheritance is sometimes called single or linear implementation inheritance. The name is appropriate, as the subclass inherits the implementation of its superclass. Java only allows single inheritance of implementation using the extends clause. Multiple inheritance of implementation occurs when a class inherits multiple implementations from the interfaces it implements, but this is not allowed in Java (p. 240).

The inheritance relationship can be depicted as an inheritance hierarchy (also called a class hierarchy), where each subclass is connected by the inheritance arrow to its direct superclass. The java.lang.Object class is always at the top (the root) of any Java inheritance hierarchy, as all classes extend (either directly or indirectly) this class. The path from a subclass in the inheritance hierarchy to the root traces all the superclasses that the subclass inherits from. Classes up in the hierarchy are more generalized (often called broader), as they abstract the class behavior. Classes lower in the hierarchy are more specialized (often called narrower), as they customize the inherited behavior by additional properties and behavior. Figure 5.1 illustrates the inheritance relationship between the class Light, which represents the more general abstraction, and its more specialized subclasses. The class SpotLightBulb inherits from the classes LightBulb, Light, and Object.

Figure 5.1 Inheritance Hierarchy