Java Polymorphism

We have discussed the concept of abstraction and its implementation through encapsulation. And then further reuse of the encapsulated data and code by inheritance. Polymorphism brings further elegance to the code, making the code more readable and maintainable.

From the programmer's point of view, it provides a single interface for similar multiple actions. The word has come from the Greek words - poly, meaning many, and morphs meaning forms. What exactly is polymorphism? Let us try to decipher now. 

Say you need to find the area of a square and a rectangle. We need one parameter for the square and two parameters for the rectangle. Need two functions and hence two function names in C to maintain separate identities in the whole application program. Looks dirty. The programmer has to remember two function names.

			

double AreaOfSquare (double side)
{
      return side * side;
}

				
			

double AreaOfRectangle(double length,double breadth)
{
      return side * side;
}

				

Polymorphism makes this easy for us. Look at the Java code. All three Java methods calculate the area, but all of them have the same name but accept different parameters. This is known as method overloading.

There is another type of overloading, which is called operator overloading. C++ supports user-defined operator overloading, but Java doesn't. Java internally does some overloading of operators like + is overloaded for concatenation. This means if you write a+b where a and b are numeric, it adds, but if one of them is non-numeric, it concatenates.

The following Java program shows polymorphic function area – 3 forms [one parameter, two int parameters, two double parameters]. All have the same name – only differ in parameter type and number.

			

class Shapes {
    public int area(int side) {
       return side * side;
    }
    public int area(int length, int breadth) {
       return length * breadth;
    }
    public double area(double length, double breadth) {
       return length * breadth;
    }
}
class Main {
  public static void main(String[] args) {
    Shapes myShape = new Shapes();  // Create a Shapes object
    int area = myShape.area(5);
    System.out.println(area);
    area = myShape.area(6,2);
    System.out.println(area);     
    double area1 = myShape.area(2.5,3.5);
    System.out.println(area1);     
  }
}

				

Types of Polymorphism

Polymorphism in Java is divided into two types – compile-time and run-time. We can perform polymorphism in Java by method overloading or method overriding. The above example is a case of method overloading. We can achieve method overloading by changing the number and/or type of the parameters of the method.

For each such variety, we have to create a separate method with the same method name but a different list of parameters as shown in the 3 area methods in above example. This is a use case of compile time polymorphism as the reference to the exact function is known at compile time itself and resolved by the Java compiler. We achieve compile-time polymorphism by method overloading.

Run-time polymorphism is different in concept. Here the polymorphic behaviour of the function is not known at compile time but is determined at the run time. Run time polymorphism is achieved by

  1. Method overriding – re-writing a base class method in the derived class
  2. Creating a derived class object by upcasting

The object is defined from the base class but created by the new operator with the derived class. Once we upcast, the call of the overridden method depends on the object on which the call is made, and that is only in run time.