Polymorphism and Inheritance
Indirect use of class members
There is a subtle feature of inheritance that is worth noting at this point. The visibility modifiers determine whether a variable or method is inherited into a subclass. If a variable or method is inherited, it can be referenced directly in the subclass by name, as if it were declared locally in the subclass. However, all variables and methods that are defined in a parent class exist for an object of a derived class, even though they can’t be referenced directly. They can, however, be referenced indirectly.
Let’s look at an example that demonstrates this situation. The program shown in Listing 7.17 contains a main method that instantiates a Pizza object and invokes a method to determine how many calories the pizza has per serving due to its fat content.
//********************************************************************
// FoodAnalysis.java Author: Lewis/Loftus
//
// Demonstrates indirect referencing through inheritance.
//********************************************************************
public class FoodAnalysis
The FoodItem class shown in Listing 7.18 represents a generic type of food. The constructor of FoodItem accepts the number of grams of fat and the number of servings of that food. The calories method returns the number of calories due to fat, which the caloriesPerServing method invokes to help compute the number of fat calories per serving.
//********************************************************************
// FoodItem.java Author: Lewis/Loftus
//
// Represents an item of food. Used as the parent of a derived class
// to demonstrate indirect referencing through inheritance.
//********************************************************************
public class FoodItem
The Pizza class, shown in Listing 7.19, is derived from FoodItem class, but it adds no special functionality or data. Its constructor calls the constructor of FoodItem, using the super reference assuming that there are eight servings per pizza.
Note that the Pizza object called special in the main method is used to invoke the method caloriesPerServing, which is defined as a public method of FoodItem and is therefore inherited by Pizza. However, caloriesPerServing calls calories, which is declared private, and is therefore not inherited by Pizza. Furthermore, calories references the variable fatGrams and the constant CALORIES_PER_GRAM, which are also declared with private visibility.
Even though Pizza did not inherit calories, fatGrams, or CALORIES_PER_GRAM, they are available for use indirectly when the Pizza object needs them. The Pizza class cannot refer to them directly by name because they are not inherited, but they do exist. Note that a FoodItem object was never created or needed.
//********************************************************************
// Pizza.java Author: Lewis/Loftus
//
// Represents a pizza, which is a food item. Used to demonstrate
// indirect referencing through inheritance.
//********************************************************************
public class Pizza extends FoodItem
{
//-----------------------------------------------------------------
// Sets up a pizza with the specified amount of fat (assumes
// eight servings).
//-----------------------------------------------------------------
public Pizza (int fatGrams)
{
super (fatGrams, 8);
}
}
*** Answer questions about each variable and method declared in the FoodItem class. Indicate whether it exists in or is inherited by the Pizza class. Note that every FoodItem member exists in the Pizza class, no matter how it is declared. The items that are not inherited can be referenced only indirectly.
polymorphism
Usually, the type of a reference variable matches exactly the class of the object to which it refers. That is, if we declare a reference as follows, the bishop reference is used to refer to an object created by instantiating the ChessPiece class.
ChessPiece bishop;
However, the relationship between a reference variable and the object it refers to is more flexible than that.
The term polymorphism can be defined as “having many forms.” A polymorphic reference is a reference variable that can refer to different types of objects at different points in time. The specific method invoked through a polymorphic reference can change from one invocation to the next.
Consider the following line of code:
obj.doIt();
If the reference obj is polymorphic, it can refer to different types of objects at different times. If that line of code is in a loop or in a method that is called more than once, that line of code might call a different version of the doIt method each time it is invoked.
At some point, the commitment is made to execute certain code to carry out a method invocation. This commitment is referred to as binding a method invocation to a method definition. In most situations, the binding of a method invocation to a method definition can occur at compile time. For polymorphic references, however, the decision cannot be made until run time. The method definition that is used is based on the object that is being referred to by the reference variable at that moment. This deferred commitment is called late binding or dynamic bind- ing. It is less efficient than binding at compile time because the decision must be made during the execution of the program. This overhead is generally acceptable in light of the flexibility that a polymorphic reference provides.
We can create a polymorphic reference in Java in two ways: using inheritance and using interfaces. This section describes how we can accomplish polymorphism using inheritance. Later in the chapter we revisit the issue of interfaces and describe how polymorphism can be accomplished using interfaces as well.
In Java, a reference that is declared to refer to an object of a particular class can also be used to refer to an object of any class related to it by inheritance. For example, if the class Mammal is used to derive the class Horse, then a Mammal reference can be used to refer to an object of class Horse. This ability is
shown in the following code segment:
Mammal pet;
Horse secretariat = new Horse();
pet = secretariat; // a valid assignment
The reverse operation, assigning the Mammal object to a Horse reference, is also valid but requires an explicit cast. Assigning a reference in this direction is generally less useful and more likely to cause problems because although a horse has all the functionality of a mammal (because a horse “is-a” mammal), the reverse is not necessarily true.
This relationship works throughout a class hierarchy. If the Mammal class were derived from a class called Animal, the following assignment would also be valid:
Animal creature = new Horse();
Carrying this to the limit, an Object reference can be used to refer to any object because ultimately all classes are descendants of the Object class. An ArrayList, for example, uses polymorphism in that it is designed to hold Object references. That’s why an ArrayList can be used to store any kind of object. In fact, a particular ArrayList can be used to hold several different types of objects at one time because, in essence, they are all Object objects.
The reference variable creature, as defined in the previous section, can be polymorphic because at any point in time it can refer to an Animal object, a Mammal object, or a Horse object. Suppose that all three of these classes have a method called move that is implemented in different ways (because the child class overrode the definition it inherited). The following invocation calls the move method, but the particular version of the method it calls is determined at runtime:
creature.move();
At the point when this line is executed, if creature currently refers to an Animal object, the move method of the Animal class is invoked. Likewise, if creature currently refers to a Mammal or Horse object, the Mammal or Horse version of move is invoked, respectively.
Of course, since Animal and Mammal represent general concepts, they may be defined as abstract classes. This situation does not eliminate the ability to have polymorphic references. Suppose the move method in the Mammal class is abstract, and is given unique definitions in the Horse, Dog, and Whale classes (all derived from Mammal). A Mammal reference variable can be used to refer to any objects created from any of the Horse, Dog, and Whale classes, and
can be used to execute the move method on any of them.
Let’s look at another situation. Consider the class hierarchy shown below. The classes in it represent various types of employees that might be employed at a particular company. Let’s explore an example that uses this hierarchy to demonstrate several inheritance issues, including polymorphism.
The Firm class shown in Listing 7.16 contains a main driver that creates a Staff of employees and invokes the payday method to pay them all. The program output includes information about each employee and how much each is paid (if anything).
The classes in this UML represent various types of staff members in a company.
The Hospital class is a main driver that creates a Staff of employees and invokes the payday method to pay them all.
The program outputs information about the staff and how much each is paid.
The Staff class maintains an array of objects that represents the staff in a hospital.
The array is declared to hold StaffCrew references, but it is actually filled with objects created from several other classes, such as Supervisor and FullTimeStaff.
These classes are all descendants of the StaffMember class, so the assignments are valid. The staffList array is filled with polymorphic references.
The payday method of the Staff class traverses the list of employees, printing their information and calling their pay methods to determine how much each employee should be paid.
The invocation of the pay method is polymorphic, because each class has its own version of the pay method.
The StaffCrew class is abstract. It is not designed to be instantiated or to be any of the members of the staff. It is designed to be a parent of all staff member classes. Each staff has a name, address, and phone number, so variables to store these values are declared in the StaffCrew class and are inherited by all descendants.
The StaffCrew class contains a toString method to return the information managed by the StaffCrew class. It also contains an abstract method called basicPay, which takes no parameters and returns a value of type double. It would not make sense to define this method in this class since it is different for every staff member.
The children classes provide the definition for basicPay so they are dynamically selected at run time. This is called polymorphism. It lets us treat similar objects in consistent but unique ways.
//********************************************************************
// Demonstrates polymorphism and inheritance.
//********************************************************************
public class Hospital
{
//-----------------------------------------------------------------
// Creates a staff for a hospital and pays them.
//-----------------------------------------------------------------
public static void main (String[] args)
{
Staff personnel = new Staff();
personnel.payday();
}
}
//********************************************************************
// Represents the personnel staff of a particular hospital.
//********************************************************************
public class Staff
{
private StaffCrew[] staffList;
//-----------------------------------------------------------------
// Constructor: Sets up the list of staff members.
//-----------------------------------------------------------------
public Staff ()
{
staffList = new StaffCrew[6];
staffList[0] = new Supervisor ("Mary", "111 ABC Rd",
"555-4444", "123456789", 2000.00);
staffList[1] = new FullTimeStaff ("John", "222 DEF Rd",
"555-5555", "234567891", 1000.00);
staffList[2] = new FullTimeStaff ("Alice", "333 GHI Rd",
"555-3333", "345678912", 1100.00);
staffList[3] = new PartTime ("Bob", "444 JKL Rd",
"555-2222", "456789123", 12.00);
staffList[4] = new Volunteer ("Tom", "555 MNO Rd",
"555-1111");
staffList[5] = new Volunteer ("Jill", "666 PQR Rd",
"555-7282");
((Supervisor)staffList[0]).grantBonus (300.00);
((PartTime)staffList[3]).addHours (25);
}
//-----------------------------------------------------------------
// Pays all staff members.
//-----------------------------------------------------------------
public void payday ()
{
double amount;
for (int count=0; count < staffList.length; count++)
{
System.out.println (staffList[count]);
amount = staffList[count].basicPay(); // polymorphic
if (amount == 0.0)
System.out.println ("Thanks!");
else
System.out.println ("Paid: " + amount);
System.out.println ("-----------------------------------");
}
}
}
//********************************************************************
// Represents any crew member.
//********************************************************************
abstract public class StaffCrew
{
protected String name;
protected String address;
protected String phone;
//-----------------------------------------------------------------
// Constructor: Sets up this staff crew using the specified
// information.
//-----------------------------------------------------------------
public StaffCrew (String eName, String eAddress, String ePhone)
{
name = eName;
address = eAddress;
phone = ePhone;
}
//-----------------------------------------------------------------
// Returns a string including the basic full time staff information.
//-----------------------------------------------------------------
public String toString()
{
String result = "Name: " + name + "\n";
result += "Address: " + address + "\n";
result += "Phone: " + phone;
return result;
}
//-----------------------------------------------------------------
// Derived classes must define the pay method for each type of
// full time staff.
//-----------------------------------------------------------------
public abstract double basicPay();
}
//********************************************************************
// Represents a staff member that works as a volunteer.
//********************************************************************
public class Volunteer extends StaffCrew
{
//-----------------------------------------------------------------
// Constructor: Sets up this volunteer using the specified
// information.
//-----------------------------------------------------------------
public Volunteer (String eName, String eAddress, String ePhone)
{
super (eName, eAddress, ePhone);
}
//-----------------------------------------------------------------
// Returns a zero pay value for this volunteer.
//-----------------------------------------------------------------
public double basicPay()
{
return 0.0;
}
}
//********************************************************************
// Represents a general paid full time staff.
//********************************************************************
public class FullTimeStaff extends StaffCrew
{
protected String IDNumber;
protected double payRate;
//-----------------------------------------------------------------
// Constructor: Sets up this full time staff with the specified
// information.
//-----------------------------------------------------------------
public FullTimeStaff (String eName, String eAddress, String ePhone,
String eIDNumber, double rate)
{
super (eName, eAddress, ePhone);
IDNumber = anIDNumber;
payRate = rate;
}
//-----------------------------------------------------------------
// Returns information about a full-time staff as a string.
//-----------------------------------------------------------------
public String toString()
{
String result = super.toString();
result += "\nID Number: " + IDNumber;
return result;
}
//-----------------------------------------------------------------
// Returns the pay rate for this full-time staff.
//-----------------------------------------------------------------
public double basicPay()
{
return payRate;
}
}
//********************************************************************
// Represents an Supervisor staff member, who can earn a bonus.
//********************************************************************
public class Supervisor extends FullTimeStaff
{
private double bonus;
//-----------------------------------------------------------------
// Constructor: Sets up this supervisor with the specified
// information.
//-----------------------------------------------------------------
public Supervisor (String eName, String eAddress, String ePhone,
String eIDNumber, double rate)
{
super (eName, eAddress, ePhone, eIDNumber, rate);
bonus = 0; // bonus has yet to be awarded
}
//-----------------------------------------------------------------
// Grants the specified bonus to this supervisor.
//-----------------------------------------------------------------
public void grantBonus (double execBonus)
{
bonus = execBonus;
}
//-----------------------------------------------------------------
// Computes and returns the pay for a supervisor, which is the
// regular full-time staff payment plus a one-time bonus.
//-----------------------------------------------------------------
public double basicPay()
{
double payment = super.basicPay() + bonus;
bonus = 0;
return payment;
}
}
//********************************************************************
// Represents a full time staff that gets paid by the hour.
//********************************************************************
public class PartTime extends FullTimeStaff
{
private int hoursWorked;
//-----------------------------------------------------------------
// Constructor: Sets up this part time staff using the specified
// information.
//-----------------------------------------------------------------
public PartTime (String eName, String eAddress, String ePhone,
String eIDNumber, double rate)
{
super (eName, eAddress, ePhone, eIDNumber, rate);
hoursWorked = 0;
}
//-----------------------------------------------------------------
// Adds the specified number of hours to this staff's
// accumulated hours.
//-----------------------------------------------------------------
public void addHours (int moreHours)
{
hoursWorked += moreHours;
}
//-----------------------------------------------------------------
// Computes and returns the pay for this part time staff.
//-----------------------------------------------------------------
public double basicPay()
{
double payment = payRate * hoursWorked;
hoursWorked = 0;
return payment;
}
//-----------------------------------------------------------------
// Returns information about this part time staff as a string.
//-----------------------------------------------------------------
public String toString()
{
String result = super.toString();
result += "\nCurrent hours: " + hoursWorked;
return result;
}
}
The Staff class shown in Listing 7.17 maintains an array of objects that represent individual employees of various kinds. Note that the array is declared to hold StaffMember references, but it is actually filled with objects created from several other classes, such as Executive and Employee. These classes are all descendants of the StaffMember class, so the assignments are valid.
The payday method of the Staff class scans through the list of employees, printing their information and invoking their “pay” methods to determine how much each employee should be paid. The invocation of the “pay” method is polymorphic because each class has its own version of the “pay” method.
The StaffMember class shown in Listing 7.18 is abstract. It does not represent a particular type of employee and is not intended to be instantiated. Rather, it serves as the ancestor of all employee classes and contains information that applies to all employees. Each employee has a name, address, and phone number, so variables to store these values are declared in the StaffMember class and are inherited by all descendants.
The StaffMember class contains a toString method to return the information managed by the StaffMember class. It also contains an abstract method called pay, which takes no parameters and returns a value of type double. At the generic StaffMember level, it would be inappropriate to give a definition for this method. The descendants of StaffMember, however, each provide their own specific definition for pay. By defining pay abstractly in StaffMember, the payday method of Staff can polymorphically pay each employee.
The Volunteer class shown in Listing 7.19 represents a person that is not compensated monetarily for his or her work. We keep track only of a volunteer’s basic information, which is passed into the constructor of Volunteer, which in turn passes it to the StaffMember constructor using the super reference. The “pay” method of Volunteer simply returns a zero pay value. If pay had not been overridden, the Volunteer class would have been considered abstract and could not have been instantiated.
Note that when a volunteer gets “paid” in the payday method of Staff, a simple expression of thanks is printed. In all other situations, where the “pay” value is greater than zero, the payment itself is printed.
The Employee class shown in Listing 7.20 represents an employee that gets paid at a particular rate each pay period. The pay rate, as well as the employee’s social security number, is passed along with the other basic information to the Employee constructor. The basic information is passed to the constructor of StaffMember using the super reference.
Related questions:
1. In what specific cases does inheritance support polymorphism? You can copy and paste the code.
2. How is overriding related to polymorphism?
3. Why is the StaffCrew class in the Hospital example declared as abstract?
4. Why is the pay method declared in the StaffCrew class, given that it is abstract and has nobody at that level?
5. Which “pay” method is invoked by the following line from the payday method of the Staff class?
amount = staffList[count].pay();