In OOPS, polymorphism is a technique by which a program can exhibit different behaviours based on some input. The concept is very similar to the principle of biological polymorphism, where a species has various forms of existence.
Polymorphism helps us to code to an interface and has multiple implementations, making the program better. In Java, there are two types of polymorphism:
We implement Compile-time polymorphism using method overloading, and the Runtime polymorphism using method overriding. We’ll learn about these two types and techniques in detail in the upcoming sections.
In compile-time polymorphism, the compiler resolves the method calls during compilation. Since this process happens statically, it is also called static polymorphism and exhibits static/early binding. In Java, it is implemented via method overloading.
Method overloading is a technique that allows multiple methods within a class to have the same name with different argument lists. See this blog to study how method overloading is achieved.
Consider a scenario where we have different methods for each kind of addition:
int addTwo (int first , int second) {
//logic
}
int addThree (int first , int second , int third) {
//logic
}
With method overloading, this can be written as:
int addition (int first , int second) {
//Implementation code
}
int addition (int first , int second , int third) {
//Implementation code
}
The main advantage of method overloading is that it enhances code readability and flexibility. The optimized code has a single method name that is, it makes more sense and increases readability. Also, the compiler resolves the calls, so we don’t need to make sure we call the proper method (like we had to do in un-optimized code); hence, flexibility is also increased.
In this type of polymorphism, the JVM resolves which block of code will be executed during runtime, also known as dynamic polymorphism. Since the resolves are made during runtime thus, it supports dynamic or late binding. In Java, it is implanted via method overriding.
Method overriding comes into the picture when we deal with inheritance. We override a method of the superclass to provide meaningful or custom implementation according to the subclass.
Example
public class Base {
//...
public void ovrMethod (int a){
//implemention
}
}
public class Child extends base {
//...
@Override
public void ovrMethod (int a){
//modified implemention
}
}
//using the methods.
//ref is assigned Child instance
Base ref = new Child();
//Child ovrMethod is called
ref.ovrMethod(0);
//ref is assigned Base instance
ref = new Base();
//Base ovrMethod is called
ref.ovrMethod(0);
Working of method overriding
Generally, we call overridden method with the base class’s reference by creating a reference of the base type and assigning it an instance of the child class (using the new keyword). To understand more about runtime polymorphism, one needs to understand typecasting in java. Refer to this blog.
When ovrMethod() is used via the reference ref, the decision of which method to call is taken at runtime. If ref is assigned an instance of the child class, then the overriding method is called; otherwise, if ref points to an instance of the base class, the overridden method is called.
In this blog, we first understood polymorphism and its importance. Further, we explored the types and techniques by which they are implemented in Java. Finally, we had a comparative study of the two types — compile-time and runtime polymorphism in almost every aspect.
Enjoy learning.