Polymorphism in Java

Polymorphism is a Greek word that means "many-shaped", referring to the ability to define different classes with identically named methods or properties that can be used interchangeably by caller code at run time.

Polymorphism has two distinct aspects:

  • At run time, objects of a sub class may be treated as objects of a base class in places such as method parameters and collections or arrays. When this occurs, the object's declared type is no longer identical to its run-time type.
  • Or another way around, at run-time, when the caller calls the method, JVM will look up the run-time type of the object, and invokes that override the method. That means, in your source code you can call a method on a base class, but in the run time the subclass's version of the method is the one that get executed.

This is because in Java, all non-static methods are by default "virtual functions." Only methods marked with the keyword final (cannot be overridden) and private methods (not inherited) are non-virtual. Base classes may define and implement methods, and sub classes can override them by providing their own definition and implementation. This will enable you to work with groups of related objects in a uniform way.

There are two types of polymorphism in Java:

  1. compile-time polymorphism
  2. runtime polymorphism.

We can perform polymorphism in java by method overloading and method overriding.

Method Overloading

Method Overloading is a feature that allows a class to have multiple methods having same name as long as their argument lists are different. For example, method draw(int height, int width) having two parameters is different with method draw(int height, int width, int length) with three parameters.

Method Overriding

Method Overriding is a feature that allow programmers to declare and override a method in sub class which is inherited from base class. Overriding is done so that a sub class can provide its own implementation to a method that already provided by it's parent (base class). The method in base class is called overridden method and the method in sub class is called overriding method.

Let's Get Dirty

OOP UML Inheritance & Polymorphism.png

(Inheritance and) Polymorphism in Polygon

As an example, let say that we need to have a functionality that enables a user to create various kinds of polygons on a drawing application. We never know which specific types of polygons the user will create. However, the application need to be able to keep track of all the various types of polygons that are created. In this case, we can use polymorphism to solve this problem in two basic steps:

  1. Create a class hierarchy in which each specific polygon class are inherited from a common base class.
  2. Override base class method in subclass. When the method is invoked in the base class, the appropriate method in subclass will be invoked.

First, create a base class called Polygon, and sub classes such as Circle, Triangle, and Rectangle. To give more picture, create another class Square which is extends from Rectangle. Give the Polygon class a method called draw, and override it in each subclass to draw the particular shape that the class represents. Later, we will call each class's draw method in our DrawingApp.

Polygon.java
public class Polygon {
        
    public void draw() {
        System.out.println("Drawing Polygon");
    }
}
                    

Circle.java
public class Circle extends Polygon {
    
    @Override
    public void draw() {
        System.out.println("Drawing Circle");
        super.draw();
    }
}
                    

Triangle.java
public class Triangle extends Polygon {
    
    @Override
    public void draw() {
        System.out.println("Drawing Triangle");
        super.draw();
    }
}
                    

Rectangle.java
public class Rectangle extends Polygon {
    
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle");
        super.draw();
    }
}
                    

Square.java
public class Square extends Rectangle {
    
    @Override
    public void draw() {
        System.out.println("Drawing Square");
        super.draw();
    }
}
                    

DrawingApp.java
import java.util.Arrays;
import java.util.List;

public class DrawingApp {
    
    public static void main(String[] args) {
        // Polymorphism #1: a Circle, Triangle, Rectangle and Square
        // can all be used whereever a Polygon is expected. No cast is
        // required because an implicit conversion exists from a subclass 
        // to its base class.
        List<Polygon> polygons = Arrays.asList(
                new Circle(), 
                new Triangle(), 
                new Rectangle(), 
                new Square());
        
        // Polymorphism #2: the method draw is
        // invoked on each of the sub classes, not the base class.
        for (Polygon p : polygons) {
            p.draw();
        }
    }
}

/* 
Output:
------    
Drawing Circle
Drawing Polygon
Drawing Triangle
Drawing Polygon
Drawing Rectangle
Drawing Polygon
Drawing Square
Drawing Rectangle
Drawing Polygon
*/
                    

When a subclass inherits from a base class, it gains all the non private methods and fields of the base class. Then, the programmer must be able to choose whether to

  • override methods from base class in the subclass, or
  • inherit the closest base class method. If the method implementation is as what the subclass needed, the subclass can use it without overriding it

A sub class that has replaced or overridden a method can still access the method on the base class using the super keyword. Example: super.draw(); as in the codes above.

Preventing Sub Classes from Overriding

A subclass can stop inheritance by declaring an override as final. This requires putting the final keyword in the class member declaration. Let's return to our Rectangle, and mark method draw() as final:

Rectangle.java
public class Rectangle extends Polygon {
    
    @Override
    public final void draw() {
        System.out.println("Drawing Rectangle");
        super.draw();
    }
}
                    

Now, during compilation for Square class, we will encounter error "draw() in Square cannot override draw() in Rectangle" overridden method is final