Poly = many, Morph = form

Polymorphism in programming is the technique of giving multiple forms to existing operators, functions, classes etc.

For example:

In C++, integer variables a and b adds to give sum of two numbers, whereas object1 and object2 adds to call a method. Hence, + operator is having multiple forms.

a = 5, b =6; a + b => 11

object1 + object2 => object1.operator+( object2 )

Java supports two type of polymorphism: Compile time & Runtime polymorphism

Compile time polymorphism

This polymorphism is called compile time because it is checked by compiler during compilation of code. In this type, we overload methods i.e. we create multiple methods with same name and different parameters.

The users of such methods don’t have to remember multiple names for similar type of operations. Here is an example:

public class Main {
   public static void main(String[] args) {
      Utility.add(43, 55);
      Utility.add(43.5f, 55.5f);
      Utility.add("Ashutosh ", "Pandey");
   }
}
class Utility{
 
   public static void add(int a,int b){
      System.out.println("Adding integers");
      System.out.println(a+b);
   }
   public static void add(float a,float b){
      System.out.println("Adding floats");
      System.out.println(a+b);
   }
   public static void add(String a,String b){
      System.out.println("Adding strings");
      System.out.println(a+b);
   }
}

So you have to call add() method for any type of addition.

Runtime polymorphism

This is called runtime because compiler is going to ignore it. It will be checked when the program is running.

In this type of polymorphism, the super type reference represents its subclasses. Let us say that we have two classes Lebrador and Bulldog. Both are dogs. So to create their objects, we normally write:

Bulldog tommy = new Bulldog();
Lebrador simba = new Lebrador();

To represent them using super type, we write:

Dog tommy  = new Bulldog();
Dog simba = new Lebrador();

Here, Dog is a super type ( class or interface ). Compiler will not complain about the above two lines for type mismatch.

So the question is, what is the advantage and why should we represent subclass by their parent type?

Let us check this normal example first.

public class Main {
   public static void main(String[] args) {
      HondaCivic civic = new HondaCivic();
      HyundaiVerna verna = new HyundaiVerna();
 
      showCar(civic);
      showCar(verna);
   }
 
   static void showCar(HondaCivic civic){
      civic.model();
      civic.price();
   }
 
   static void showCar(HyundaiVerna verna){
      verna.model();
      verna.price();
   }
}
interface Car{
   void model();
   void price();
}
class HondaCivic implements Car{
   @Override
   public void model() {
      System.out.println("Honda Civic LX");
   }
   @Override
   public void price() {
      System.out.println("$ 18,000");
   } 
}
class HyundaiVerna implements Car{
   @Override
   public void model() {
      System.out.println("Hyndai Verna 4S Fluidic");
   }
   @Override
   public void price() {
      System.out.println("$ 12,000");
   } 
}

We intentionally created the interface Car so that all classes should have similar methods.

The problem with the above code is that we have to create n number of showCar() methods for n number of classes.

We can avoid creation of multiple methods with the help of runtime polymorphism. Here is the updated code:

public class Main {
   public static void main(String[] args) {
      Car civic = new HondaCivic();
      Car verna = new HyundaiVerna();
 
      showCar(civic);
      showCar(verna);
   }
 
   static void showCar(Car car){
      car.model();
      car.price();
   }
}
interface Car{
   void model();
   void price();
}
class HondaCivic implements Car{
   @Override
   public void model() {
      System.out.println("Honda Civic LX");
   }
   @Override
   public void price() {
      System.out.println("$ 18,000");
   } 
}
class HyundaiVerna implements Car{
   @Override
   public void model() {
      System.out.println("Hyndai Verna 4S Fluidic");
   }
   @Override
   public void price() {
      System.out.println("$ 12,000");
   } 
}

This time a single showCar() method can handle all type of cars.