Polymorphism in Java

Polymorphism is a Greek word that means "many-shaped", it's refers to the ability to define multiple classes with functionally different, yet identically named methods or properties that can be used interchangeably by client code at run time.

Polymorphism has two distinct aspects:

  • At run time, objects of a derived 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.
  • In Java, all non-static methods are by default "virtual functions." Only methods marked with the keyword final, which cannot be overridden, along with private methods, which are not inherited, are non-virtual. Base classes may define and implement methods, and sub classes can override them, which means they provide their own definition and implementation. At run-time, when client code calls the method, the JVM looks up the run-time type of the object, and invokes that override the method. Thus in your source code you can call a method on a base class, and cause a subclass's version of the method to be executed.

This will enable you to work with groups of related objects in a uniform way.

There are two types of polymorphism in Java: compile-time polymorphism and 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 more than one method having the same name, if their argument lists are different. For example the argument list of a method draw(int height, int width) having two parameters is different from the argument list of the method draw(int height, int width, int length) having three parameters.

Method Overriding

Method Overriding is a feature that allows programmer to declare and override a method in sub class which is already present in parent class. Overriding is done so that a child class can give its own implementation to a method which is already provided by the parent class. In this case the method in parent class is called overridden method and the method in child 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 derives 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 sublass 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
*/
                    

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.

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

  • override methods in the base class, or
  • inherit the closest base class method without overriding it

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