Session 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’s 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 object that is used, because each subclass overrides and changes some of the behavior from the superclass. This ability for the same code to be used with several 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("Lisa","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, 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 an Person
as a parameter, returns an 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, 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 is passed. Polymorphism gives us this flexibility. The last token of 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 code in the printInfo
method are polymorphic because their behavior will take 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 (not its instance) 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 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 know to Employee; results in an error System.out.println("Dictation: " + e.takeDictation("Hello")); }
Session 5 |