Chapter 5 |
Polymorphism
One of the most powerful benefits of inheritance is that it allows client class to treat different kinds of objects in the same way. For example, with the person class hierarchy described earlier, it is possible for client class to create an array or other data structure that contains both persons and students, and then perform operations on each element of that array. The client class will behave differently depending on the type of the object used, because each subclass overrides and changes some of the behavior from the superclass. The ability to use the same code with different types of objects is called polymorphism.
The word polymorphism literally means "having many forms". In Java, it means that one variable might be used with several objects of related classes at different times in a program.
Polymorphism is made possible by the fact that the type of a reference variable (one that refers to an object) does not have to exactly match the type of the object it refers to. More specifically, it is legal for a superclass variable to refer to an object of its subclass. The following is a legal assignment statement:
Person p1 = new Student("Maria", "University of Tartu");
When we were studying the primitive types, we saw cases in which a variable of one type could store a value of another type (for example, an int value can be stored in a double variable). In the case of primitive values, however, Java converts variables from one type to another: int values are automatically converted to doubles when they are assigned.
When a subclass object is stored in a superclass variable, no such conversion occurs. The object referred to by p1
really is a Student
object, not an Person
object. If we call methods on it, it will behave like a Student
object.
This ability for variables to refer to subclass objects allows us to write flexible code that can interact with many types of objects in the same way. For example, we can write a method that accepts a Person
as a parameter, returns a Person
, or creates an array of Person
objects. In any of these cases, we can substitute a Student
or other subclass object of Person
, and the code will still work. Even more importantly, the code will actually behave differently depending on which type of object is used, because each subclass overrides and changes some of the behavior from the superclass. This is polymorphism at work.
Study the following example:
// A class to represent employees in general. class Employee { public int getHours() { return 40; } public double getSalary() { return 40000.0; } } // A class to represent lawyers. class Lawyer extends Employee { // overrides getSalary from Employee class public double getSalary() { return 80000.0; } // this is the Lawyer's added behavior public void sue() { System.out.println("I'll see you in court!"); } } // A class to represent secretaries. class Secretary extends Employee { // this is the Secretary's added behavior public void takeDictation(String text) { System.out.println("Dictating text: " + text); } } // Demonstrates polymorphism by passing many types of employees // as parameters to the same method. public class Test { // Prints information about any kind of employee. public static void printInfo(Employee e) { System.out.print("Hours: " + e.getHours() + ", "); System.out.print("Salary: " + e.getSalary() + ", "); System.out.println(e); } public static void main(String[] args) { Employee edna = new Employee(); Employee lucy = new Lawyer(); // polymorphism Secretary stan = new Secretary(); // create a list to perform different operations Employee[] staff = {edna, lucy, stan}; for (Employee e : staff) { printInfo(e); } } }
The program output is:
Hours: 40, Salary: 40000.0, Employee@73f792cf Hours: 40, Salary: 80000.0, Lawyer@2ed94a8b Hours: 40, Salary: 40000.0, Secretary@38082d64
Notice that the method let us pass many different types of Employee
as parameters, and it produces different behavior depending on the type that was passed to it. Polymorphism gives us this flexibility. The last token of the output printed for each employee is the employee object itself, which calls the toString method. Our classes do not have toString methods, so the program uses the method of the Object
class.
The word polymorphism comes from the Greek words "poly" and "morph", which mean "many" and "forms", respectively. The lines of the code in the printInfo method are polymorphic because their behavior takes many forms depending on what type of employee is passed as the parameter.
The program does not know which getSalary method to call until it is actually running. When the program reaches a particular call to an object’s method, it examines the actual object to see which method to call. This concept has taken many names over the years, such as late binding, virtual binding, and dynamic dispatch.
Note that when we send messages to an object stored in a variable of a superclass type, it is legal only to call methods that are known to the superclass (not to the subclasses). For example, the following code will not compile because the Employee
class has no takeDictation method:
public static void printInfo(Employee e) { System.out.print("Hours: " + e.getHours() + ", "); System.out.print("Salary: " + e.getSalary() + ", "); System.out.println(e); // toString representation of employee // takeDictation() is not known to Employee; results in an error System.out.println("Dictation: " + e.takeDictation("Hello")); }
Chapter 5 |