Inheritance and Polymorphism

Inheritance allows one class (the subclass) to use and modify the public variables and methods of another class (the superclass). Usually, the subclass is a specialized version of the more general superclass, an is-a relationship. For example, a square is a rectangle, which in turn is a polygon. This allows software reuse, for example, the code to compute the area or perimeter of a polygon should also be able to compute the area/perimeter of a rectangle or a square. However, a rectangle might have its own code to compute the area that is more efficient than the more general polygon code.

Polymorphism is writing code using a superclass that can be executed for any object of a subclass. For example, an array of Polygons might include Rectangle objects, Square objects, Triangle objects, etc. (Violet UML Diagram)

Each subclass of Polygon might inherit or have its own method for computing the area. The following code might be written to sum up the area of these objects:

double sumArea = 0;
for (Polygon p : polygonArray) {
    sumArea += p.computeArea();
}

Each call to computeArea uses the computeArea method of that object. For example, if p is a Triangle object, then this code uses the computeArea method of Triangle.

The Employee and Payable Case Study

This case study combines the two case studies in Chapter 10 of the textbook. Here is the Eclipse project and the documentation. The class hierarchy is as follows (Violet UML Diagram):

Not shown in the UML class diagram is the following. Each abstract class and concrete class has a constructor and methods for getting and setting its fields (getters and setters) and a toString method. Also, each concrete class implements the getPaymentAmount method.

The PayrollSystemTest Main Method

The details of the main methods are discussed first, followed by how other classes implement the appropriate behavior.

The main method in PayrollSystemTest.java uses the Employee class and its subclasses. It first creates four objects, one for each subclass of Employee. Note that the constructors have different numbers of parameters. For example, a BasePlusCommissionEmployee object will need information for six fields (one in BasePlusCommissionEmployee class and five in superclasses). Also, the BasePlusCommissionEmployee constructor needs some way to initialize the private variables from the superclasses.

The next group of statements prints information about each object individually. For this to work, each subclass needs a toString method and a getPaymentAmount method.

The next group of statements (creation of array through the first for loop) prints information about each object polymorphically. The objects are stored in an Employee array. The for loop will depend on the same toString and getPaymentAmount methods as the previous group of statements. An if statement inside the for loop illustrates how to write code specific to one of the Employee subclasses. The if condition tests whether currentEmployee belongs to the subclass. The first statement inside the if casts the Employee object to the subclass. This is needed because setBaseSalary and getBaseSalary are methods in the BasePlusCommissionEmployee class, but not of Employee or other subclasses of Employee.

The last for loop illustrates how to find out the specific class for each object.

The PayableInterfaceTest Main Method

The main method in PayrollSystemTest.java uses the Payable interface and its subclasses. It creates four objects, two are Invoice objects and two are SalariedEmployee objects. Again, the number of parameters in the constructors is related to the number of fields that need to be initialized.

The four objects are stored in a Payable array, and the for loop iterates over the elements of this array. This is another example of polymorphism. The toString and getPaymentAmount methods are to be found in the class of each object, not the Payable interface.

The Payable Interface

An interface declares one or more methods, but does not implement the methods. For example, the Payable interface declares the getPaymentAmount method, but provides no code to implement it.

The Invoice and Employee Classes

You can declare classes that implement one or more interfaces. For example, both the Employee class and Invoice class specify implements Payable, which is a promise to implement the getPaymentAmount method. This allows Employee objects and and Invoice objects to be processed polymorphically as Payable objects because they all implement the getPaymentAmount method.

Invoice is a concrete class with four fields, a constructor that initializes them, getters and setters, a toString method and a getPaymentAmount method. Note that toString and getPaymentAmount use the get methods rather than directly accessing the variables.

The Employee class is a little more complicated. It is an abstract class because it does not implement the getPaymentMethod method. This means that you cannot create an object whose class is Employee. For example:

Employee employee007 = new Employee("James", "Bond", "007-00-7007");
would cause a compile error.

One might wonder what the constructor is good for. This code will be called by the constructors of the subclasses.

Subclasses of Employee

Each of the subclasses have the following features:

Other Examples

The project file inheritance.zip contains more examples of inheritance. Rational.java in the rational package illustrates defining a subclass of Number, an existing Java class. The classes in the range package illustrate defining an interface, an abstract class, and a concrete class. The classes in the other two packages will be discussed when we discuss Java Generics.