Polymorphism in Java
data Polymorphism is a process of representing one in many forms. In this article, we will see everything about polymorphism in Java.
- It creates different forms of a single resource or a single member.
- Most importantly, it creates different forms, not different copies!
- Java supports two types of polymorphism:
Compile time polymorphism in Java:
Overloading portrays the concept of compile time polymorphism. Two types of overloading are available in Java:
Method overloading in Java:
- When a class has multiple methods by the same name but has different parameters, then we call it method overloading.
- For example, suppose you have to perform the addition of numbers and you have to define a method for that. The method can have any number of arguments for addition.
- Now you can do it in 2 ways:
- The first one is giving different names to the methods based on the parameters.
- Suppose you write two methods such as:
- addTwo(int, int) for two parameters, and
- addThree(int, int, int) for three parameters,
- It might be difficult to understand the behavior of the method because the name differs.
- The second way is to give the same name to the methods and perform method overloading. This will provide clarity and help us to understand the method easily.
Overloading is only possible within a single class.
Advantage of method overloading:
- If we use method overloading, then there is no need for creating entirely different methods that do essentially the same thing!
- It, in turn, provides a simpler interface of instances of a class to objects of other classes.
- Thus method overloading improves the readability of the program.
Rules for method overloading:
There are two ways to overload the methods of Java:
- By changing the number of arguments.
- By changing the data type.
Note: In Java, Method Overloading is not possible by changing the return type of the method.
Example of overloading method by changing the number of arguments:
public class Calculation { void sum(int a, int b) { System.out.println("The sum of a and b: " + (a + b)); } void sum(int a, int b, int c) { System.out.println("\nThe sum of a,b and c: " + (a + b + c)); } public static void main(String[] args) { Calculation obj = new Calculation(); obj.sum(10, 20); obj.sum(10,20,30); } }
Output:
Example of overloading method by changing datatypes:
public class CalculationData { void sum(int a, int b) { System.out.println("The sum of a and b from int type: " + (a + b)); } void sum(double a, double b) { System.out.println("\n The sum of a and b from double type: " + (a + b)); } public static void main(String[] args) { CalculationData obj = new CalculationData(); obj.sum(10, 20); obj.sum(10. 5, 20. 25); } }
Output:
Method overloading is not possible by changing return types:
This is because ambiguity may occur within the datatypes.
public class CalculationReturnType { double sum(int a, int b) { System.out.println("The sum of a and b from double type: " + (a + b)); return (a+b); } void sum(int a, int b) { System.out.println("The sum of a and b from int type: " + (a + b)); } int sum(int a, int b) { System.out.println("The sum of a and b from int type: " + (a + b)); return (a + b); } public static void main(String[] args) { CalculationReturnType obj = new CalculationReturnType(); obj.sum(10, 20); obj.sum(2, 4); obj.sum(10, 80); } }
Output:
All the above three methods have a different return type. But when those methods are called inside the main() method, we can clearly see that only the method having the largest datatype as return type has been executed for all the three times.
This is basically due to implicit type casting available in Java!
In the above diagram, we can see
- A byte can be promoted to short, int, long, float or double.
- The data type short and char can be promoted to int, long, float or double and so on.
Constructor overloading:
- Constructors, like other methods, can take varying numbers and types of parameters, enabling you to create your objects with exactly the properties you want it to have.
- Constructor overloading implies that a class can have any number of constructors that differ in parameter lists.
package test_2; public class StudentConstructor { int id; String name; int age; public StudentConstructor(int i, String n) { id = i; name = n; } public StudentConstructor(int i, String n, int ag) { id = i; name = n; age = ag; } void display() { System.out.println("The details are: " + id + " " + name + " " + age); } public static void main(String[] args) { StudentConstructor s1 = new StudentConstructor(111, "Geeta"); StudentConstructor s2 = new StudentConstructor(222, "Reeta", 25); s1.display(); s2.display(); } }
Output:
How does Overloading work?
- While defining multiple methods with the same name, you define separate method signatures with different sets of arguments and different definitions.
- When you call a method through an object, Java figures out which definition to execute based on the type and number of arguments with which you called it.
- Basically, Java matches up to the method name as well as the type and number of arguments to choose which method definition to execute.
- Java can understand method overloading as long as each parameter list is unique for each method name.
Runtime Polymorphism in Java:
- Java supports runtime polymorphism using method overriding.
- In the process of Runtime Polymorphism or dynamic method dispatch, a call to an overridden method is resolved at runtime rather than compile time.
Method overriding in Java:
- Sometimes, we want an object to respond to the same methods but have different behavior when that method is called. To achieve this, you can use method overriding.
- Overriding a method involves having the same method with the same signature in the subclass as declared in the parent class. To explain, we use method overriding when we want to provide a specific implementation to any method of parent class within the subclass.
- And when that method is called, the method in the subclass is found and executed instead of the one in the superclass.
Rules for method overriding:
- The method must have the same name as in parent class.
- The method must have the same parameter as in the parent class.
Upcasting:
Before going to overriding, let us first understand what is upcasting.
When a reference variable of parent class refers to the object of the child class, it is known as upcasting.
For example,
class AA { // statements to be executed } class BB extends AA { AA ob = new BB(); // upcasting }
Need for method overriding:
Here is an example to show the need for method overriding:
package test_2; public class Vehicle { void run() { System.out.println("Vehicle is running"); } } public class Bike extends Vehicle { void displayColour() { System.out.println("Bike is of black colour"); } public static void main(String[] args) { Vehicle obj1 = new Bike(); obj1.run(); } }
It gives output as:
Now let’s say we want to provide a specific implementation to run() method in the subclass so that it behaves in a different way. We can do this by method overriding.
For that, we will define the new implementation of run() inside the subclass Bike.
Note: An overridden method is called using the concept of upcasting. The reference variable of a superclass is used to call the method within the subclass.
The reference variable of superclass Vehicle calls the method run() within the subclass Bike.
package test_2; public class Vehicle { void run() { System.out.println("Vehicle is running"); } } package test_2; public class Bike extends Vehicle { void run() { System.out.println("Bike is running safely"); } void displayColour() { System.out.println("Bike is of black colour"); } public static void main(String[] args) { Vehicle obj1 = new Bike(); obj1.run(); } }
Output:
Why do we need upcasting if we can directly call it with the subclass object?
- The object of upcasting will only have those methods that are present in the superclass as well as have an implementation in the subclass.
- It will not acquire the methods that are only present in the subclass.
public class Vehicle { void run() { System.out.println("Vehicle is running"); } } public class Bike extends Vehicle { void displayColour() { System.out.println("Bike is of black colour"); } public static void main(String[] args) { Vehicle obj1 = new Bike(); obj1.run(); obj1.display(): } }
If you call any subclass method that is not available in the superclass, you will get a compile-time error as follows:
Here is another example of method overriding!
public interface Student { void calculateMarks(); } public interface Teacher { void subjectSpecification(); } import java.util.*; public class AssistantTeacher implements Student, Teacher { public void subjectSpecification() { System.out.println("Enter the subject specification" + ""); Scanner sc = new Scanner(System.in); String subject = sc.nextLine(); System.out.println("Subject specification: "+subject); } public void calculateMarks() { System.out.println("\nEnter the marks"); Scanner sc = new Scanner(System.in); int mark1 = sc.nextInt(); int mark2 = sc.nextInt(); int result = mark1 + mark2; System.out.println("Total marks: "+ result); } public static void main(String[] args) { AssistantTeacher obj = new AssistantTeacher(); obj.subjectSpecification(); obj.calculateMarks(); } }
Output:
Runtime polymorphism with data member:
You can override methods but you cannot override data members of a class. The value of the data member will remain the same. So data members cannot achieve runtime polymorphism.
package test_2; public class BikeClass { int speedlimit = 90; } package test_2; public class Honda extends BikeClass{ int speedlimit = 150; public static void main(String[] args) { BikeClass obj = new Honda(); System.out.println("The speed limit is: " + obj.speedlimit); //parent class value Honda obj1 = new Honda(); System.out.println("The speed limit is: " + obj1.speedlimit); //child class value BikeClass obj2 = new BikeClass(); System.out.println("The speed limit is: " + obj2.speedlimit); //parent class value } }
Output:
Difference between method overloading and method overriding:
Method Overloading | Method Overriding |
---|---|
Used to increase the readability of the program | Used to provide the specific implementation of the method in a subclass that is already present in its superclass |
Performed within the same class | Occurs in two classes that have IS-A relationship i.e. in case of inheritance |
Parameters or arguments must be different | Parameters must be the same |
There is a difference in the signature of the methods | The method signature is the same for both the methods |
In my next article, I will discuss abstract classes and interfaces.