What Is Polymorphism In Java – Tutorial With Examples

This tutorial explains what is Polymorphism in Java, types of polymorphism, & how to implement compile-time polymorphism with examples:

The word “Polymorphism” derives from two words i.e. “Poly” which means many and “morphs” meaning forms. Thus polymorphism means many forms. In a programming language, we can say that an object can take many forms, and hence the object is polymorphic.

Polymorphism is one of the most important features of OOP and it allows us to express a feature (an object or a message or method or an operator) in various forms.

=> Take A Look At The Java Beginners Guide Here.

What is Polymorphism in Java

Introduction To Polymorphism In Java

The polymorphic entity behaves differently under different scenarios.

For example, consider a ‘+’ (addition) operator in Java. This is a binary operator and takes two operands. When the operands that are passed to the ‘+’ operator are numbers, an addition operation that returns the sum of two numbers is performed.

When operands change to String type, the ‘+’ operator does not add the String objects, but concatenates or joins the contents of the string to form a resultant third string.

For example, if “one” and “two” are the contents of two String objects then “one” + “two” will result in “onetwo”. This is concatenation.

In Java, all the objects are polymorphic as they all are derived from the “Object” class and thus fulfill the ‘IS-A” relationship with the Object class.

An object is always accessed through a reference variable of that particular class type. Once a reference variable of a particular type is declared, it cannot be modified. However, if the reference variable is not declared as ‘Final’ then we can reassign it to point to other objects.

The type of this object reference will decide the class methods or functions that should be called.

For example, if there is an ABC class and class XYZ derived from ABC, the both classes have a polymorphic method func ().

class ABC{

 void func(){}

}

class XYZ extends ABC{

 void func() {}

}

Let’s create a reference of type ABC.

 obj = new XYZ ();

Now when we call the func () method, since the object pointed to by obj is of class XYZ, the func () method from the class XYZ will be invoked.

As we have seen in the above example, the method func () has different implementations but the same prototype. Depending on the object pointed by reference object, we have the appropriate implementation in that scenario invoked. This is polymorphism.

Let’s discuss Polymorphism in Java in detail.

Java Polymorphism Example

Let’s understand a simple example of polymorphism in Java with the addition operation as discussed earlier.

Here we use two methods with the same name but different parameters. The first function accepts two integer parameters and the second method accepts two string parameters.

Depending on the type of parameters passed, the appropriate method gets called and either adds two integers and prints the result or concatenates the two strings and prints the resultant string.

The Java program is given below:

class Addition_operation{
    //method to add two integers
    void addition_func(int num1,int num2){
      System.out.println("ABC::addition_func:" + (num1+num2));
    }
    //overloaded method to add two strings
    void addition_func(String str1, String str2){
        String result = str1 + " " + str2;
        System.out.println("XYZ::addition_func:" + result);
    }
    
}

public class Main
{
	public static void main(String[] args) {
		Addition_operation abc = new Addition_operation();  //create a class object
		abc.addition_func (3,4);        //calls 1st method
		abc.addition_func ("Hello" , "World!");     //calls 2nd method
        
	}
}

Output:

Output - Polymorphism example

Here we have seen that when the first time we pass two integer parameters to the addition_func, then the first method is called. In the second function call, we pass two String type parameters, and hence the second overloaded method is called.

Types Of Polymorphism

Java supports two types of Polymorphism:

  • Compile-time polymorphism
  • Run-time polymorphism

Types of polymorphism in Java

As the name suggests, the compile-time polymorphism is performed at compile-time and run-time polymorphism is done at runtime.

As shown in the above figure, Compile-time polymorphism is implemented through Overloading. We can overload either the method or operator. Runtime polymorphism is achieved through Overriding.

In this tutorial, we will discuss Compile-time polymorphism in detail. We will take up runtime polymorphism in the next tutorial.

Compile-Time Polymorphism In Java

Compile-time polymorphism is also known as “Static polymorphism”. As part of compile-time polymorphism, whatever polymorphism is to be performed, is performed at compile time.

In Java, the compile-time polymorphism is performed using “Method Overloading”. Using method overloading, we can have one or more methods with the same name and only differentiated on numbers or type or order of parameters.

Method overloading is the most common implementation of compile-time polymorphism in Java. Java also supports operator overloading.

What is Overloading in Java in general?

Overloading in Java is a process of having more than one method with the same name and return type but differing on the sequent, number, and types of arguments. It is also called method overloading in general.

Method Overloading In Java

Method overloading is an implementation of compile-time polymorphism in Java. When we have one or more methods with the same name and/or return types but different parameter lists, then we say we have “overloaded” the methods.

So in a given class, we can have various methods with the same name but different argument lists.

How do we invoke overloaded methods? Or how does the compiler know which method is to be called?

The invocation of the exact method that matches the call is performed depending on the parameter list.

We have already seen that a class in Java can have more than one constructor. In the case of constructors, the arguments list or the arguments that the constructor accepts is different in all constructors. This is an example of overloading. So constructor overloading is a basic form of method overloading in Java.

Now let’s see how to Overload a Method in Java?

Java provides three ways of method overloading depending on the variations in the parameter/argument list.

#1) Type Of Parameters

We can overload methods in Java depending on the data type of parameters.

Consider the following example wherein we have given prototypes of three methods.

addition (int, int);
addition (int, float);
addition (String, String);

As seen from above, we have the same method name in all the three cases and the same number of parameters but each method call has different types of parameters.

So as long as methods have different types of parameters, we can say that the methods are overloaded. When we invoke the method, the compiler decides on the data type of the parameter, and depending on the data type of the parameter list provided with the method call, the appropriate method is called.

For example, if we have a method call like below:

addition (3, 3.5);

In the above method call, we can see that the first parameter is int type while the second parameter is float type. When the above call is encountered, the compiler resolves the parameter list and then invokes the appropriate method which is the second method above.

Now let’s implement a complete Java program to demonstrate the method overloading based on the data types of parameters.

class MethodOverload
{
    //overloaded method - char parameter
public void printParam(char ch)
    {
 System.out.println("Input character:" + ch);
    }
    //overloaded method - int parameter
public void printParam(int num)  
    {
System.out.println("Input Number:" + num);
    }
}
class Main
{
public static void main(String args[])
   {
MethodOverload obj = new MethodOverload();
obj.printParam('A');     //call overloaded method (char )
obj.printParam(10);  //call overloaded method (int )
   }
}

Output:

Type of parameters - output

#2) Number Of Parameters

Another implementation of method overloading is by overloading methods with a different number of parameters in the function call.

For example, let’s consider the following method declarations:

addnum (int, int);

addnum (int, int, int);

In the above method declarations, the first method declaration has two parameters and the second declaration has three parameters. When a function is invoked, the compiler inspects the number of parameters and then resolves the method call appropriately.

The below example shows the program that uses method overloading based on the number of parameters.

class MethodOverload
{
    //overloaded method - 1 parameter
public void printParam(char ch)
    {
System.out.println(ch);
    }
    //overloaded method - 2 parameters
    public void printParam(char ch, int num)  
    {
System.out.println("Character: " + ch + " ; "+ "Number:" +num);
    }
}
class Main
{
public static void main(String args[])
   {
MethodOverload obj = new MethodOverload();
obj.printParam('A');     //call overloaded method (1 )
obj.printParam('A',10);  //call overloaded method (2 )
   }
}

Output:

number of parameters - output

In this example, we have two methods overloaded based on the number of parameters. The first method takes one parameter and the second method takes two parameters. In the main method, we call both the methods one after another and the compiler resolves the function call depending on the number of parameters specified.

#3) Sequence Of Parameters

The third approach of implementing method overloading is based on the sequence of parameters in overloaded methods.

Consider the following example of the method declaration,

sum(int, float);
sum(float,int);

Here we have an overloaded method sum. In the first declaration, the parameters are int and float. In the second declaration as well, the parameters are int and float but their order in the parameter list is changed.

Now the float parameter appears first while the int parameter is second. We can achieve method overloading, by changing the parameter order.

The below Java program demonstrates this.

class MethodOverload {
    //overloaded method - char,int parameter
public void printParam(char ch, int num)    {
System.out.println("Input character:" + ch + " ; " + "Input Number:" + num);
    }
    //overloaded method - int,char parameter
public void printParam(int num, char ch)      {
System.out.println("Input Number:" + num + " ; " + "Input Character:" + ch);
    }
}
class Main
{
public static void main(String args[])
   {
MethodOverload obj = new MethodOverload();
obj.printParam('A', 100);     //call overloaded method (char,int)
obj.printParam(100, 'A');  //call overloaded method (int,char)
   }
}

Output:

Sequence of parameters - output

In the above program, we have a printParam overloaded method that has int and char parameters whose order is changed in two method declarations.

Invalid Cases Of Method Overloading

So far we have discussed method overloading using parameter list. We have not yet considered the return type of method. Note that we do not overload methods based on return types.

For instance, if two methods have the same name and same parameter lists but different return types, then we don’t say that these two methods are overloaded. This case becomes invalid for overloading.

So if we have the following declarations:

int sum(int, int);
String sum(int, int);

In this case, the compiler will issue an error as two methods are not overloaded. Hence solely based on the return types, the methods are not differentiated.

Let’s demonstrate this invalid case using a Java program.

class OverloadDemo  {
    public double myMethod(int num1, int num2)    {
     System.out.println("OverloadDemo::myMethod returns double");
     return num1+num2;
   }
   public int myMethod(int var1, int var2)    {
      System.out.println("OverloadDemo::myMethod returns int");
      return var1-var2;
   }
}
class Main {
   public static void main(String args[])
   {
      OverloadDemo obj2= new OverloadDemo();
      obj2.myMethod(10,10);
      obj2.myMethod(20,12);
   }
}

In the above program, the method myMethod has two prototypes. One prototype takes two int parameters and returns double. The second method prototype takes two int parameters and returns an int.

So when we compile this program, we get the below output.

Output:

Invalid case of Method Overloading

The above compilation error indicates that the method is declared twice. This means that the compiler does not consider these methods overloaded merely based on the return type.

Operator Overloading

Operator overloading is an overloading mechanism in which a different meaning is given to an existing operator.

As we have discussed in the introduction section of this tutorial, an addition operator ‘+’ is a classic example of operator overloading.

When the operands of this operator are numeric, then the + operator returns the total of two values. But when the operands are of String type, then the result of the addition operation is the concatenated string. Note that In Java, we can only overload + (addition) operator.

This operator performs two functions:

  • Adding integers or numeric values.
  • Concatenating strings

Hence operator overloading support is limited in Java unlike in C++ wherein we can overload almost all the operators barring a few like sizeof, dot operator, etc.

The below program demonstrates Operator Overloading in Java.

class OperatorOverload { 
    //overloaded method for concatenating two strings
    void operator(String str1, String str2) 
    { 
        String resultStr = str1 + str2; 
        System.out.println("Concatenated String: "
                           + resultStr); 
    } 
    //overloaded method for adding two numbers  
    void operator(int num1, int num2) 
    { 
        int result = num1 + num2; 
        System.out.println("Sum of two numbers : " + result); 
    } 
} 
  
class Main { 
    public static void main(String[] args) 
    { 
        OperatorOverload obj = new OperatorOverload(); 
        obj.operator(10, 15);       //add two numbers
        obj.operator("Hello ", "World!!");  //concatenate two strings 
    } 
}

Output:

Operator Overloading - Output

In the above program, we have overloaded the ‘+’ operator. When we pass two integer values to the overloaded method, a sum of two integers is returned and when two Strings are passed the result is the concatenated string.

Certain points to be noted regarding overloading and compile-time polymorphism.

  1. Method overloading is the way to implement compile-time polymorphism that is also known as Static polymorphism.
  2. Static polymorphism is also known as early binding or compile-time binding.
  3. As binding of parameters and function call happens at compile-time, overloading is called compile time binding.
  4. We can only overload ‘+’ operator in Java and it performs the addition of two integers or concatenation of two strings.

Frequently Asked Questions

Q #1) Why do we need Polymorphism in Java?

Answer: Polymorphism allows us to have many implementations for one object. Through method overloading, we need not have too many methods with different names which are difficult to remember. We can instead have overloaded methods so that we can have a clear implementation of the methods performing similar functions.

Also overriding aids in the proper implementation of inheritance that allows us to add more functionality to the existing classes in an easier manner.

Q #2) What is Polymorphism OOP?

Answer: The Object-oriented programming definition of polymorphism refers to the ability of the programming languages to implement one object in various forms. Polymorphism is also defined as the ability of a program to override the methods from the parent class into the derived class to have additional functionality.

Q #3) Can we overload and override the main method?

Answer: No. We cannot override the static main method. Though we can overload the main method, JVM will never call the overloaded main method. So the best answer is not to overload or override the main method.

Q #4) Can Constructors be Overloaded?

Answer: Yes, we can overload the constructors in Java in the same way we overload the Java methods. Constructors usually have the same name but a different number of arguments.

Q #5) Why is Method Overloading useful?

Answer: We can write clean code using method overloading and it also becomes readable as we have methods with the same name. So if we are implementing functionality for various data types, then we can overload the methods and it will be easier to separate the code.

Conclusion

Polymorphism in Java means that an object can have many forms. Polymorphism in Java has two types i.e. Compile-time polymorphism and Runtime polymorphism. Compile-time polymorphism is done at compile time. Compile-time polymorphism is static and is implemented through method overloading and operator overloading.

Runtime polymorphism is performed at runtime and is dynamic. It is implemented using method overriding.

In this tutorial, we have seen the ways to implement method overloading. We have also discussed operator overloading in detail. Java supports the overloading of ‘+’ operator only.

=> Read Through The Easy Java Training Series.