The ability to point overridden methods of subclass using the reference variable of super class is called as Polymorphism.
A super class reference variable can store subclass object address, as subclass maintains IS-A relation with super class. However, a subclass reference variable cannot store super class reference. For example, Circle is a Shape, so you can store Circle’s instance in Shape’s reference variable, but Shape is not Circle, so you can’t store Shape’s instance in Circle’s reference variable.
If a subclass overrides a method defined in super class, a super class reference variable can invoke it provided an instance of subclass is stored in it.
In other words, a super class reference variable can access subclass methods polymorphically only if subclass overrides the methods of super class.
package com.techstackjournal.polymorphism;
public class Shape {
public void draw() {
System.out.println("Preparing the canvas...");
}
public void paint() {
System.out.println("Painting the canvas...");
}
}
package com.techstackjournal.polymorphism;
public class Rectangle extends Shape {
public void draw() {
System.out.println("Drawing Rectangle...");
}
public void fillColor(String color) {
System.out.println("Filling Color " + color);
}
}
package com.techstackjournal.polymorphism;
public class Application {
public static void main(String[] args) {
Shape shape = new Rectangle();
shape.draw();
shape.paint();
// shape.fillColor();
}
}
- In this example, we are creating an instance of Rectangle, but the reference variable is of type Shape.
- Even though the reference variable is of type Shape, when you call draw method, it will invoke subclass draw method. Because we are storing subclass instance in that reference variable.
- Shape class reference variable cannot call fillColor because it is not defined in super class
Super class type Arguments and Return Type
We can also have super class type as arguments and return type for a method. It allows the method to be scalable.
package com.techstackjournal.polymorphism.example2;
public abstract class Shape {
public abstract void draw();
}
package com.techstackjournal.polymorphism.example2;
public class Rectangle extends Shape {
public void draw() {
System.out.println("Drawing Rectangle...");
}
}
package com.techstackjournal.polymorphism.example2;
public class Circle extends Shape {
public void draw() {
System.out.println("Drawing Circle...");
}
}
package com.techstackjournal.polymorphism.example2;
public class CompositeShape extends Shape {
public void draw() {
System.out.println("Drawing CompositeShape...");
}
public Shape superImpose(Shape shape) {
shape.draw();
return this;
}
}
- CompositeShape class has a method called as superImpose, which takes Shape as an argument and also returns a Shape
- Since, Rectangle and Circle inherit from Shape, both can be passed into superImpose method
- In superImpose method, it calls the draw method of the Shape.
- So at runtime, if Circle object is passed to the superImpose method, draw method invocation on Shape will call Circle’s draw method
package com.techstackjournal.polymorphism.example2;
public class Application {
public static void main(String[] args) {
CompositeShape cshape = new CompositeShape();
cshape.superImpose(new Rectangle());
Shape shape = cshape.superImpose(new Circle());
shape.draw();
}
}
Drawing Rectangle...
Drawing Circle...
Drawing CompositeShape...
The returned Shape object by superImpose method is of type CompositeShape. Due to this, the draw method that gets executed will be of CompositeShape.
Dynamic Method Dispatching
When a super class reference invokes a method which is overridden by subclass, Java will decide which version of the method to be executed at runtime, this is called as Dynamic Method Dispatching, which is also called as Runtime Polymorphism.
Runtime Polymorphism vs Compile-time Polymorphism
- An old school thought is that Polymorphism is of two kinds
- Runtime Polymorphism – Which overridden method to run is decided at runtime. It is also called as late binding or dynamic linking
- Compile time Polymorphism – Which overloaded method to call is decided at compiling the program itself. It is also called as early binding or static linking
Finding the Type of an Object
In the superImpose method, say you want to add some logic when the shape is Circle.
To check the type of any object we can use instanceOf operator.
package com.techstackjournal.polymorphism.example2;
public class CompositeShape extends Shape {
public void draw() {
System.out.println("Drawing CompositeShape...");
}
public Shape superImpose(Shape shape) {
shape.draw();
if (shape instanceof Circle) {
System.out.println("Doing something special for Circle object...");
}
return this;
}
}