Inheritance is not limited to just one level. Classes can be organized into an hierarchy, which can be a number of levels deep (see Figure 4.1). A subclass can be at the end of a chain of superclasses, with each class in the chain being able to add variables and add or override methods. The immediate superclass of a class (the one which it extends) can be referred to as the direct superclass, while others higher up in the chain are indirect superclasses. Whilst it is possible to construct long chains of these relationships, it is a general guideline that there should not be too many layers of relationships otherwise it can become difficult to reason about them. The overriding principle is, of course, to properly and completely model and describe the problem at hand.
In any inheritance hierarchy, as we move up the hierarchy, classes typically change from being concrete to abstract. A concrete class is meant to have objects instantiated from it, and must therefore provide a complete implementation. Abstract classes, on the other hand, do not provide complete implementations. Parts of the representation of abstract classes act as place holders for additional variables and methods to be added by subclasses. An abstract class cannot be instantiated.
This now gives us two kinds of classes to design; those which add to a partial implementation and are designed to be inherited from, and those which provide complete implementations that may include parts inherited from a superclass.
A particular role of an abstract class is to specify a public method interface which can be inherited by direct or indirect subclasses. This means that all the subclasses of the abstract class will share the same interface (and possibly extend it in their own way). The methods declared by the abstract class are typically overridden by the subclasses, to specialize their behaviour. The declarations of the methods in the abstract class may provide default implementations or simply have empty method bodies.
What advantage does this give? What we want to be able to do is substitute subclass objects for abstract superclass objects; this is the polymorphism feature of inheritance that we want. Unfortunately, on the face of it, there is a problem since there are no superclass objects. However, it turns out that this is not a problem, as we shall see in Chapter 5. We are able to do exactly what we want which is to write code using the superclass type and expect that code to work with any subclass object that is actually supplied. To put this idea into perspective, consider the following example.
Copyright © 1997 Russel Winder and Graham RobertsLast updated: 6 Oct 1997