What Is Compile-Time And Run-Time Polymorphism In Java?

What Is Compile-Time And Run-Time Polymorphism In Java?
What Is Compile-Time And Run-Time Polymorphism In Java?

Introduction 

Another pearl in the ocean of object-oriented programming is polymorphism. It is so essential that languages that do not allow polymorphism cannot be called Object-Oriented. Java is one of the many object-oriented programming languages that allows polymorphism.

Polymorphism comprises two Greek words: poly and morphs, “poly” means many, and “morphs” means forms. Therefore, Polymorphism signifies many forms.

What is Polymorphism?

Polymorphism is the capability of an object of a class to behave differently in response to a message or an action.

It occurs when the same object acts differently depending on the situation. We can gain flexibility in our code by utilising polymorphism, which allows us to do different actions by employing the same names based on the needs. 

For instance, ‘Human’ is a subclass of a ‘Mammal’. Similarly, ‘Dog’, ‘Cat’ are also subclasses of ‘Mammal’. Mammals can see through daylight. So, if the message “day vision” is passed to all mammals, they all behave alike. 

On the other hand, if the object “night vision” is passed to cats only, humans and Dogs will not be able to view at night, whereas cats will have vision during the night also. Here cats can behave differently than other mammals in response to a message or action. This is polymorphism.

Depending on how the class method is called, Polymorphism in Java can be categorised into two types:

  • Compile-time polymorphism
  • Runtime polymorphism

Let’s dig a little deeper into compile-time and run-time polymorphism in Java:

Compile-Time Polymorphism

The compiler examines the method signature at compile time and determines which method to invoke for a given method call. Since it is exhibited during compile time, it is called Compile-Time Polymorphism. It is also known as static polymorphism, early binding or overloading. 

Method Overloading is one of the ways to implement compile-time polymorphism in Java. Constructor Overloading and Method Hiding are two more examples of this polymorphism.

Method overloading

Method Overloading occurs when a class has many methods with the same name but different parameters or different data types. 

What are the benefits of method overloading? It improves the readability of the programme.

In Java, we have two ways to overload the method.

  • By changing the number of parameters
  • By changing the data types of parameters

Method 1: By changing the number of parameters

import java.util.*;

class example
{

  // First function
  public static int sum (int x, int y){
    return x + y;
  }

  // Second function
  public static int sum (int x, int y, int z){
     return x + y + z;
  }

  public static void main (String[] args){
    // First function called
    System.out.println (sum (10, 20));

    // Second function called
    System.out.println (sum (10, 20, 30));
  }
}

 Output:

30
60

In the Example class, there are two methods with the same name: “sum“. The first sum method takes two integers as input and returns their total. On the other hand, the second sum method takes three integers and returns the sum of those three values.

Both the methods are called in the main, and the compiler decides which method will be invoked at compile time.

Method 2: By changing the data types of parameters

class Example
{
    public void display(String s){
         System.out.println(s);
     }
    public void display(int i){
         System.out.println(i);
     }
}

class Result
{
    public static void main (String[] args) 
     {
           Example obj = new Example();
           obj.display("Codingninjas");
           obj.display(5);
     }
}

Output:

Codingninjas
5

In the above example, method overloading is achieved by changing the data type of the parameters. For example, one display() method accepts string parameters while another display() method takes the int parameter.

Can we overload a method by changing its return type? 

Method overloading in java is impossible by changing the method’s return type as there may occur ambiguity. 

The example given below depicts how ambiguity may occur:

class AdditioneExample 
{
  int add(int p,int q)
  {
    System.out.println(p+q);
  }
  double add(int p, int q)
  {
    System.out.println(p+q);
  }

  public static void main(String args[])
 {
  AdditioneExample obj = new AdditioneExample();
  int output = obj.add(10,20);                     //compilation error occurs 
 }
}

Output:

Main.java:9: error: method add(int,int) is already defined in class AdditionExample
  double add(int p, int q)
               ^
1 error

Here, a compilation error occurs, so this depicts that even after changing the return types of the methods, we cannot achieve compile-time polymorphism. 

Run-Time Polymorphism

A polymorphism that collects the information to call a method during runtime. It is also known as dynamic, late binding or overriding. In addition, it is called run-time polymorphism as it is exhibited at runtime. Java Virtual Machine (JVM) determines which method call will be invoked with the method body at runtime.

Method overriding is used to implement dynamic or runtime polymorphism in Java. A derived or child class overrides a base or a parent class method to offer a more specialised implementation. The method is the same in both parent and child classes, and the only difference is how it is implemented. 

Before moving forward, let’s take a look at upcasting:

Upcasting occurs when a reference variable in the Parent class refers to an object in the Child class. In Java, we use this to resolve the overridden methods at run time.

The Example given below depicts how upcasting is done. 

class Parent {}
class child extends Parent {}

class Test {
    public static void main(String[] args) {
        Parent P = new child();                       //Here upcasting is done
    }
}

Here, upcasting happens when the superclass named parent’s reference variable refers to the object of its subclass child. This is the way we override methods from the base class to the inherited classes to achieve runtime polymorphism.

Let’s look at some examples of runtime polymorphism:

Example 1:

//Defining a parent class 
class Country{
 
    // Implementing a method
    public void method ()
    {
        System.out.println("India");
    }
}
 
// Defining a child class
class State extends Country {
 
    // Overriding the parent method
    public void method ()
    {
        System.out.println("Delhi");
    }

    
    public static void main(String[] args)
    {
         Country obj = new State();
         obj.method();
    }
}

Output:

Delhi

When an overridden method is invoked via a parent class reference, the object type decides which method is invoked. Since both the child and parent classes have the same method. JVM determines which version of the method (child class or parent class) will be invoked at runtime.

Example 2:

class Animal{
  public void eat (){
    System.out.println ("parent class");
  }
}
class Cat extends Animal{
  public void eat (){
    System.out.println ("first inherited class");
  }
}
class Dog extends Animal{
  public void eat (){
    System.out.println ("second inherited class");
  }
}
class Test{
  public static void main (String[]args){
     Animal a;
      a = new Cat ();
      a.eat ();
      a = new Dog ();
      a.eat ();
  }
}

Output:

first inherited class
second inherited class

Here, we have derived two classes from the base class by overriding the same method from the base class into inherited classes. In the example given above, we achieved runtime polymorphism without using upcasting.

Runtime Polymorphism In Java with Data Members

In Java, we can override the methods but not the data members. So runtime polymorphism can not be achieved by overriding the data members.

The following example explains it: 


import java.util.*;
class Teacher{
    int age = 30;
}

class Child extends Teacher{
    int age = 10;
}

class Main{
    public static void main(String[] args)
    {
        Teacher obj  = new Child();
        System.out.println(obj.age);
    }
}

Output:

30

In the example given above, both the classes have a data member age. The data member is accessed via the Parent class’s reference variable, which refers to the subclass object. Because we are accessing a data member which is not overridden, it will always access the data member of the Parent class.

Compile-time vs Run-time Polymorphism

Let’s quickly look at some fundamental differences between compile-time and run-time Polymorphism in Java:

S.No.Compile-time PolymorphismRun-time Polymorphism
1.The method to be executed is chosen during compile time.The method is chosen during runtime by JVM.
2.Inheritance is not involved.Inheritance is involved.
3.It has faster execution, as it is known early during compilation.It has slower execution as compared to compile-time, as it is known during runtime.
4.It is achieved by Method Overloading.It is achieved by Method Overriding.
5.Also known as Static or Early Binding.Also known as Dynamic or Late Binding.

Frequently Asked Questions

What are compile-time and run-time polymorphism examples?

Method Overloading is the example of compile-time and run-time polymorphism on the other hand has Method Overriding as its example.

How does the Java compiler differentiate between methods in compile-time polymorphism?

Java determines which method to invoke at compile time by looking at the method signatures. Compile-time polymorphism is also known as static or early binding.

Why is overriding called runtime polymorphism?

You create a new definition for the same function signature when you override a function. It is just the kind of object or pointer that determines which function is invoked. As a result, you’re calling various function definitions with the same function name. The same data is being used for multiple purposes.

Can we override the static method? Explain in detail?

No, we cannot override the static method as the concept of Method Overriding is based on Dynamic Binding at runtime whereas static methods are bonded at compile time using static binding. Therefore, though we can specify static methods in the subclass with the same signature, this is not considered overriding because there would be no run-time polymorphism.

What happens when we override a final method?

A child class cannot override a final method declared in the Parent class. This is because the compiler will throw an error if we try to override the final method at compilation time. Hence, the methods that are declared as final cannot be Overridden or hidden.

Can we override the main method?

No, we cannot override the main method in Java because it’s a static method. A static method is linked to a class in Java, whereas a non-static method is linked to an object. As a result, overriding the main method in Java is not feasible.

Key Takeaways

Polymorphism is one of the most critical aspects of Object-Oriented Programming. By coming to the end of this article, we understood the two forms of polymorphism: compile-time and run-time polymorphism in java. Both compile-time and run-time polymorphism differ in their approach to method binding and method invocation. 

By Mehak Goel