Polymorphism in Java

Overview
Polymorphism is one of the main aspects of Object-Oriented Programming(OOP). The word polymorphism can be broken down into Poly and morphs, as “Poly” means many and “Morphs” means forms. In simple words, we can say that ability of a message to be represented in many forms.
Introduction
Let us understand the definition of polymorphism by an example; a lady can have different characteristics simultaneously. She can be a mother, a daughter, or a wife, so the same lady possesses different behavior in different situations.
Another example of polymorphism can be seen in carbon, as carbon can exist in many forms, i.e., diamond, graphite, coal, etc. We can say that both woman and carbon show different characteristics at the same time according to the situation. This is called polymorphism.
The definition of polymorphism can be explained as performing a single task in different ways. A single interface having multiple implementations is also called polymorphism.
How can polymorphism be achieved in Java?
Polymorphism in Java can be achieved in two ways i.e., method overloading and method overriding.
Polymorphism in Java is mainly divided into two types.
- Compile-time polymorphism
- Runtime polymorphism
Compile-time polymorphism can be achieved by method overloading, and Runtime polymorphism can be achieved by method overriding. In the further article, we will be discussing all the topics related to polymorphism in Java in more detail.
Polymorphism in Java Example
The below code is the basic example of polymorphism in Java.
Output
Explanation
In the above example, the Child class extends the Parent class, and the print method is overridden, this represents run-time polymorphism in Java.
In the overload class, the statement function is overloaded, this represents compile-time polymorphism in Java.
Example of polymorphism in real-life
We can relate polymorphism in real life by the following example. Consider a parent class as living thing.
Living things exist on the planet in the forms of human beings, animals, plants, bacteria, etc. These are the child classes inherited from the parent class, i.e., living things.
The code below will show you the practical implementation of the real-life example.
Output
Explanation
In the above example, the parent class Livingthings has the method live(). Subclasses of Livingthings i.e., Animals, Humanbeing, and Plants have their way of living. By the principle of inheritance and polymorphism, each subclass can define its way of living with the help of the live() method.
Types of Polymorphism in Java
There are two main types of polymorphism in Java.
1. Compile-time polymorphism
This type of polymorphism in Java is also called static polymorphism or static method dispatch. It can be achieved by method overloading. In this process, an overloaded method is resolved at compile time rather than resolving at runtime.
Method overloading
Consider a class where multiple methods have the same name. It will be difficult for the compiler to distinguish between every method.
To overcome this problem, we pass a different number of arguments to the method or different types of arguments to the method. In this way, we achieve method overloading.
In other words, a class can have multiple methods of the same name, and each method can be differentiated either by bypassing different types of parameters or bypassing a different number of parameters.
Example 1
Passing different numbers of arguments to the function.
Output
Explanation
In the above example, the CompileTime class has two functions, both having the same name, but the first function has a single argument to pass, and another one has two arguments to pass.
Here, the perimeter() function can calculate the perimeter of square and rectangle. In this way, two functions having the same name are distinguished, and compile-time polymorphism is achieved.
For the first time, when we call the perimeter method, we pass a single integer to the method, so the first method is evoked, and for the second time, we pass two integers to the method, and this time the second method is evoked.
Example 2
Passing different types of argument to the function.
Output
Explanation
In the above example, the CompileTime class has two functions, both having the same name, but in the first function, we pass a string and long as an argument, and in the second function, we pass two strings as an argument.
It shows that we can save the person's contact by mobile number or email. In this way, compile-time polymorphism is achieved.
For the first time, when we call the contact() method, we pass a name and mobile number (string and long) as arguments, so the first contact method is evoked.
The second time while calling the contact method, we pass a name and email (string and string) as arguments, so the second contact method is evoked.
2. Runtime Polymorphism
Runtime polymorphism is also called Dynamic method dispatch. Instead of resolving the overridden method at compile-time, it is resolved at runtime.
Here, an overridden child class method is called through its parent's reference. Then the method is evoked according to the type of object. In runtime, JVM figures out the object type and the method belonging to that object.
Runtime polymorphism in Java occurs when we have two or more classes, and all are interrelated through inheritance. To achieve runtime polymorphism, we must build an "IS-A" relationship between classes and override a method.
Method overriding
If a child class has a method as its parent class, it is called method overriding.
If the derived class has a specific implementation of the method that has been declared in its parent class is known as method overriding.
Rules for overriding a method in Java
- There must be inheritance between classes.
- The method between the classes must be the same(name of the class, number, and type of arguments must be the same).
Example 1: Animal class
Output
Explanation
In the above example, the Animal class is parent class, and Dog, Horse, Rabbit are its derived class where the place() method is overridden.
We have created an instance of the Animal class. When an object of every child class is created, the method inside the child class is called. As, the parent class method is overridden by child class.
Example 2: Shape Class
Output
Explanation
In the above example, the Shape class is parent class and Square, Rectangle, and Circle are its derived class where the area() method is overridden.
Example 3: Bank Class
Output
Explanation
In the above example, the Bank class is parent class and ICICI, SBI, and HDFC are its derived class where the rate() method is overridden.
Example 4: Using Data Members
Output
Explanation
In the above example, we can see that value in the child class is not overrode, as the "value" remains 50 after overriding. The above program concludes that data members are not overridden, so data members can't achieve polymorphism.
Example 5: Multilevel Inheritance
Output
Explanation
In the above example, the Animal class extends the Tiger class and the Tiger class extends the Cub class. move() function is overridden in the Tiger class and the Cub class.
Characteristics of Polymorphism
Besides method overloading and method overriding, polymorphism has other characteristics as follows.
- Coercion
- Internal Operator Overloading
- Polymorphic Variables or Parameters
- Subtype polymorphism
1. Coercion
Let us understand this with an example, consider a variable whose data type is an integer and another variable whose data type is double. If we add these two numbers, we will get a type error.
Java has inbuilt functionality called coercion to avoid such errors, where a smaller data type is automatically typecasted into a more significant data type according to need. In this case, the integer value will be typecasted to double value, and then addition takes place. Hence type error is avoided.
The implicit conversion of one data type into another without changing its context is known as coercion. This type of conversion occurs to prevent type errors.
In other words, if the datum is present in one data type, but its context requires a different data type, then Coercion occurs.
Example
Output
Explanation
In the above program, we can see that the int value “num” is added to the String “and”, then the compiler implicitly converts the int value into a string value to avoid type error.
2. Internal Operator Overloading
As explained in the article that Java does not support operator overloading. Still, there is a concept called internal operator overloading in Java, where an operator is used in more than one way. The characteristic of static polymorphism is observed here.
In Java, the ‘+’ symbol is used to add two numbers or used to concatenate two strings.
Example
Output
Explanation
In the above example, the ‘+’ operator is performing both addition and concatenation tasks.
3. Polymorphic Variables or Parameters
As I explained, the example of a lady is in the definition of polymorphism. If a lady has a child, she is called a mother; if she has a sibling, then she is called a sister; if she has a husband, she is called a wife. So here, a lady has different values under different situations; hence lady can be called a polymorphic variable.
Variables having different values under different circumstances is called polymorphic variable. It can be said that every object or instance variable in Java is a polymorphic variable because every object or instance variable has an IS-A relationship with its own classes and sub-classes.
Variables holding different values at the execution time are known as polymorphic variables.
A field name can be associated with several types, and a method name can be associated with different parameters and return types according to parametric polymorphism.
Example: Polymorphic variable
Output
Explanation
Here, the ob object is a polymorphic variable because the same object refers to parent class (India) and child class (Maharashtra).
Example: Polymorphic parameter
Output
Explanation
The above code is an example of parametric polymorphism. In the “Child” class, we define data as a string and later as an integer. Use this Java Compiler to compile your code
4. Subtype polymorphism
Let us consider an example, suppose a zoo has four distinct tigers, three distinct lions, and two distinct elephants. We are supposed to store the data of all the animals in a list. We can store the data by using the property of subtype polymorphism. Let us assume tiger, lion, and elephant are derived classes of parent class animal.
Traditionally we create an object of tiger class and store the information. Similarly, we do this for all the animals. But in subtype polymorphism, we make an array of animal class, and then we upcast objects of every tiger, lion, and elephant to animal class. Store the upcasted objects in an array of animal class.
The ability to use the subclass instead of the superclass is called subtype polymorphism. In other words, subtype polymorphism is about upcasting and late binding. Upcasting can be termed as typecasting a child object to a parent object, and late binding is simply dynamic binding or overriding.
Read the following examplke for better under standing.
Example
Output
Explanation
In the above example, the declaration of the Shape array illustrates upcasting. The Square, Circle, and Rectangle references are stored in Shape[0], Shape[1], and Shape[2] are upcast to type Shape. Each of Shape[0], Shape[1], and Shape[2] is regarded as a Shape.
Late binding is explained by the Shape[i].area(); method. When i equals 0, the compiler-generated instruction causes Square's area() method to be called. When i equals 1, Circle's area() method is called, and for i=2, Rectangle's area() method is called. This is a nature of subtype polymorphism.
Advantages of Polymorphism
- Code reusability is the main advantage of polymorphism; once a class is defined, it can be used multiple times to create an object.
- In compile-time polymorphism, the readability of code increases, as nearly similar functions can have the same name, so it becomes easy to understand the functions.
- The same method can be created in the child class as in the parent class in runtime polymorphism.
- Easy to debug the code. You might have intermediate results stored in arbitrary memory locations while executing code, which might get misused by other parts of the program. Polymorphism adds necessary structure and regularity to computation, so it is easier to debug.
Problems with Polymorphism
- Implementing code is complex because understanding the hierarchy of classes and its overridden method is quite difficult.
- Problems during downcasting because implicitly downcasting is not possible. Casting to a child type or casting a common type to an individual type is known as downcasting.
- Sometimes, when the parent class design is not built correctly, subclasses of a superclass use superclass in unexpected ways. This leads to broken code.
- Runtime polymorphism can lead to the real-time performance issue (during the process), it basically degrades the performances as decisions are taken at run time because the machine needs to decide which method or variable to invoke.
Conclusion
- Polymorphism is one of the four parts of object-oriented programming.
- With proper implementation of polymorphism, one can achieve flexible and extensible class designs.
- There are two main types of polymorphism i.e. runtime polymorphism and compile-time polymorphism.
- Runtime polymorphism is achieved through method overriding, and compile-time polymorphism is achieved through method overloading.
- Java doesn't support operator overloading but does internal operator overloading.
- Other than the runtime and compile-time, java supports coercion polymorphism and parametric polymorphism.
- Using the concept of method overriding and inheritance enables us to reuse code without re-compilation.
- Closely related functions can be accessed through the common name using the method overloading technique.
- Internal operator overloading is achieved using coercion polymorphism.
- Method overriding provides a feature where a subclass can use all the general definitions provided by the superclass and add specialized definitions through overridden methods.
FAQs
Q. What are 2 Types of Polymorphism?
A. Following are the two types of polymorphism
- Compile-time polymorphism
- Runtime polymorphism
Q. What are Polymorphism variables?
A. During the execution of a program, a polymorphic variable is defined as a variable that can hold values of several types. In other words, variables having different values under different conditions or variables holding different values at the execution time are called Polymorphism variables.
Q. What is polymorphism in programming?
A. Polymorphism is an ability of an object, variable, and function to take on multiple forms. The ability to apply a single method to derived classes and get a suitable result is called polymorphism.
Q. Why use Polymorphism in Java?
A. As we know java supports inheritance where properties of one class are derived into another class. Polymorphism uses the properties of inheritance to perform many tasks that's why we should use polymorphism in Java.
Q. What is Static Polymorphism?
A. Processes in static polymorphism are exhibited at compile time. The compiler knows which method has been called. Method overloading is an example of static polymorphism. Static polymorphism is also known as compile-time polymorphism. It can be achieved through method overloading.
Q. What is Dynamic Polymorphism?
A. In dynamic polymorphism instead of resolving the overridden method at compile-time, it is resolved at runtime. The compiler doesn't know which method has been called at compile-time. JVM decides which method is called at runtime. Dynamic polymorphism is also called as runtime polymorphism. It can be achieved through method overriding.
Q. What is the difference between Static & Dynamic Polymorphism?
A.
Static Polymorphism | Dynamic Polymorphism |
---|---|
It is achieved through method overloading. | It is achieved through method overriding. |
It is called compile-time polymorphism. | It is called runtime polymorphism. |
Known as static binding or early binding. | Known as dynamic binding or late binding. |
It has a high execution speed. | It has a low execution speed. |