Session 5 |
Polymorphism
It is important to remember that the only possible way to access an object is through a reference variable. A reference variable can be of only one type. Once declared, the type of a reference variable cannot be changed.
A reference variable can refer to any object of its declared type or any subtype of its declared type. A reference variable can be declared as a class or interface type.
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 in Java has two types: compile time polymorphism (static binding) and runtime polymorphism (dynamic binding). Method overloading is an example of static polymorphism, while method overriding is an example of dynamic polymorphism.
As we know, method overloading means there are several methods present in a class having the same name but different types/order/number of parameters. At compile time, Java knows which method to invoke by checking the method signatures. So, this is called compile time polymorphism or static binding. The concept will be clear from the following example:
class DemoOverload { public int add(int x, int y) { //method 1 return x+y; } public int add(int x, int y, int z) { //method 2 (overloading method 1) return x+y+z; } } class Test{ public static void main(String[] args) { DemoOverload test = new DemoOverload(); System.out.println(test.add(2,3)); //method 1 called System.out.println(test.add(2,3,4)); //method 2 called } }
In the above example, there are two versions of add
methods. The first method takes two parameters while the second one takes three. The compiler looks at the method signature and decides which method to invoke for a particular method call at compile time.
Suppose a subclass overrides a particular method of the superclass. For example, in the program we create an object of the subclass and assign it to the superclass reference. Now, if we call the overridden method on the superclass reference then the subclass version of the method will be called.
Have a look at the following example.
class Vehicle { public void move() { System.out.println("This is my vechile!!"); } public String toString(){ System.out.println("This is a test message."); return "abc"; } } class Bicycle extends Vehicle { public void move() { // overridden method System.out.println("Bicycle is a vechile which has two tyres!!"); } } class Test{ public static void main(String[] args) { Vehicle myVechile = new Bicycle(); // polymorphism myVechile.move(); // Bicycle is a vechile which has two tyres!! myVechile = new Vehicle(); myVechile.move(); // prints This is my vechile!! Object testVehicle = new Bicycle(); testVehicle.toString(); // dynamic binding which prints This is a test message. } }
It should be noted that in the first call to move()
, the reference type is Vehicle and the object being referenced is Bicycle
. So, when a call to move()
is made, Java waits until runtime to determine which object is actually being pointed to by the reference. In this case, the object is of the class Bicycle
; hence the method of this class is called.
In the second call of move()
method, the object is of Vehicle
class; hence, the method of class Vehicle
is called. As the method to call is determined at runtime, this is called dynamic binding or late binding.
Also pay attention to lines 26 and 27. Since Bicycle
class does not have toString
method, Java looks for it in its superclass. As the method is present in the parent class, it is used; otherwise, Java would look for it in the upper classes of the hierarchy.
It is always important to keep in mind that dynamic binding is independent of the variable type - the most important is the type of the instance. Each instance remembers precisely which class instance it is (it is fixed at the moment when the instance is created) and therefore it know where to look for the overridden methods.
Method overriding forms the basis for one of Java’s most powerful concepts: dynamic method dispatch - the mechanism by which a call to an overridden method is resolved at run time rather than compile time. Dynamic method dispatch is important because this is how Java implements run-time polymorphism.
Let’s begin by restating an important principle: a superclass reference variable can refer to a subclass object. Java uses this fact to resolve calls to overridden methods at run time. Here’s how. When an overridden method is called through a superclass reference, Java determines which version of that method to execute based upon the type of the object being referred to at the time the call occurs. Thus, this determination is made at run time.
When different types of objects are referred to, different versions of an overridden method will be called. In other words, it is the type of the object being referred to (not the type of the reference variable) that determines which version of an overridden method will be executed. Therefore, if a superclass contains a method that is overridden by a subclass, then when different types of objects are referred to through a superclass reference variable, different versions of the method are executed.
Session 5 |