What is Polymorphism in Java?
Polymorphism in Java is the task that performs a single action in different ways.
So, languages that do not support polymorphism are not ‘Object-Oriented Languages’, but ‘Object-Based Languages’. Ada, for instance, is one such language. Since Java supports polymorphism, it is an Object-Oriented Language.
Polymorphism occurs when there is inheritance, i.e., many classes are related.
Inheritance is a powerful feature in Java. Java Inheritance lets one class acquire the properties and attributes of another class. Polymorphism in Java allows us to use these inherited properties to perform different tasks. Thus, allowing us to achieve the same action in many different ways.
What is Polymorphism?
The derivation of the word Polymorphism is from two different Greek words- poly and morphs. “Poly” means numerous, and “Morphs” means forms. So, polymorphism means innumerable forms. Polymorphism, therefore, is one of the most significant features of Object-Oriented Programming.
Must Learn Core Java Topics
Real-Life Examples of Polymorphism
An individual can have different relationships with different people. A woman can be a mother, a daughter, a sister, and a friend, all at the same time, i.e. she performs other behaviors in different situations.
The human body has different organs. Every organ has a different function to perform; the heart is responsible for blood flow, the lungs for breathing, the brain for cognitive activity, and the kidneys for excretion. So we have a standard method function that performs differently depending upon the organ of the body.
Polymorphism in Java Example
A superclass named “Shapes” has a method called “area()”. Subclasses of “Shapes” can be “Triangle”, “circle”, “Rectangle”, etc. Each subclass has its way of calculating area. Using Inheritance and Polymorphism means, the subclasses can use the “area()” method to find the area’s formula for that shape.
class Shapes {
public void area() {
System.out.println("The formula for area of ");
}
}
class Triangle extends Shapes {
public void area() {
System.out.println("Triangle is ½ * base * height ");
}
}
class Circle extends Shapes {
public void area() {
System.out.println("Circle is 3.14 * radius * radius ");
}
}
class Main {
public static void main(String[] args) {
Shapes myShape = new Shapes(); // Create a Shapes object
Shapes myTriangle = new Triangle(); // Create a Triangle object
Shapes myCircle = new Circle(); // Create a Circle object
myShape.area();
myTriangle.area();
myShape.area();
myCircle.area();
}
}
Output:
The formula for the area of the Triangle is ½ * base * height
The formula for the area of the Circle is 3.14 * radius * radius
class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
class Square extends Shape {
@Override
public void draw() {
System.out.println("Drawing a square");
}
}
class Main {
public static void main(String[] args) {
Shape s1 = new Circle();
Shape s2 = new Square();
s1.draw(); // Output: "Drawing a circle"
s2.draw(); // Output: "Drawing a square"
}
}
In this example, we have a base class Shape
with a single method draw()
that prints “Drawing a shape” to the console. We then create two subclasses, Circle
and Square
, that override the draw()
method to print “Drawing a circle” and “Drawing a square” respectively.
In the main
method, we create two instances of the Shape
class, s1
and s2
, which are actually instances of the Circle
and Square
subclasses. When we call the draw()
method on these objects, the correct implementation is called based on the actual type of the object, this is run-time polymorphism. The program will output: “Drawing a circle” and “Drawing a square”
In this example, the draw()
method is overridden in the subclasses, and this allows for the program to determine which method to use at runtime. This is known as runtime polymorphism or dynamic polymorphism, Because at runtime the JVM determines the actual type of the object and calls the corresponding method.
Also Read: OOPs concepts in Java
Types of Polymorphism
You can perform Polymorphism in Java via two different methods:
- Method Overloading
- Method Overriding
What is Method Overloading in Java?
Method overloading is the process that can create multiple methods of the same name in the same class, and all the methods work in different ways. Method overloading occurs when there is more than one method of the same name in the class.
Example of Method Overloading in Java
class Shapes {
public void area() {
System.out.println("Find area ");
}
public void area(int r) {
System.out.println("Circle area = "+3.14*r*r);
}
public void area(double b, double h) {
System.out.println("Triangle area="+0.5*b*h);
}
public void area(int l, int b) {
System.out.println("Rectangle area="+l*b);
}
}
class Main {
public static void main(String[] args) {
Shapes myShape = new Shapes(); // Create a Shapes object
myShape.area();
myShape.area(5);
myShape.area(6.0,1.2);
myShape.area(6,2);
}
}
Output:
Find area
Circle area = 78.5
Triangle area=3.60
Rectangle area=12
What is Method Overriding in Java?
Method overriding is the process when the subclass or a child class has the same method as declared in the parent class.
Example of Method Overriding in Java
class Vehicle{
//defining a method
void run(){System.out.println("Vehicle is moving");}
}
//Creating a child class
class Car2 extends Vehicle{
//defining the same method as in the parent class
void run(){System.out.println("car is running safely");}
public static void main(String args[]){
Car2 obj = new Car2();//creating object
obj.run();//calling method
}
}
Output:
Car is running safely
Also, Polymorphism in Java can be classified into two types, i.e:
- Static/Compile-Time Polymorphism
- Dynamic/Runtime Polymorphism
What is Compile-Time Polymorphism in Java?
Compile Time Polymorphism In Java is also known as Static Polymorphism. Furthermore, the call to the method is resolved at compile-time. Compile-Time polymorphism is achieved through Method Overloading. This type of polymorphism can also be achieved through Operator Overloading. However, Java does not support Operator Overloading.
Method Overloading is when a class has multiple methods with the same name, but the number, types, and order of parameters and the return type of the methods are different. Java allows the user freedom to use the same name for various functions as long as it can distinguish between them by the type and number of parameters. Check out some of the important questions on run time polymorphism in java interview questions.
Example of Compile-Time Polymorphism in Java
We will do addition in Java and understand the concept of compile time polymorphism using subtract()
package staticPolymorphism;
public class Addition
{
void sum(int a, int b)
{
int c = a+b;
System.out.println(“ Addition of two numbers :” +c); }
void sum(int a, int b, int e)
{
int c = a+b+e;
System.out.println(“ Addition of three numbers :” +c); }
public static void main(String[] args)
{
Addition obj = new Addition();
obj.sum ( 30,90);
obj.sum(45, 80, 22);
}
}
The output of the program will be:
Sum of two numbers: 120
Sum of three numbers: 147
In this program, the sum() method overloads with two types via different parameters.
This is the basic concept of compile-time polymorphism in java where we can perform various operations by using multiple methods having the same name.
What is Runtime Polymorphism in Java?
Runtime polymorphism in Java is also popularly known as Dynamic Binding or Dynamic Method Dispatch. In this process, the call to an overridden method is resolved dynamically at runtime rather than at compile-time. You can achieve Runtime polymorphism via Method Overriding.
Method Overriding is done when a child or a subclass has a method with the same name, parameters, and return type as the parent or the superclass; then that function overrides the function in the superclass. In simpler terms, if the subclass provides its definition to a method already present in the superclass; then that function in the base class is said to be overridden.
Also, it should be noted that runtime polymorphism can only be achieved through functions and not data members.
Overriding is done by using a reference variable of the superclass. The method to be called is determined based on the object which is being referred to by the reference variable. This is also known as Upcasting.
Upcasting takes place when the Parent class’s reference variable refers to the object of the child class. For example:
class A{}
class B extends A{}
A a=new B(); //upcasting
Examples of Runtime Polymorphism in Java
Example 1:
In this example, we are creating one superclass Animal and three subclasses, Herbivores, Carnivores, and Omnivores. Subclasses extend the superclass and override its eat() method. We will call the eat() method by the reference variable of Parent class, i.e. Animal class. As it refers to the base class object and the base class method overrides the superclass method; the base class method is invoked at runtime. As Java Virtual Machine or the JVM and not the compiler determines method invocation, it is, therefore, runtime polymorphism.
class Animal{
void eat(){
System.out.println("Animals Eat");
}
}
class herbivores extends Animal{
void eat(){
System.out.println("Herbivores Eat Plants");
}
}
class omnivores extends Animal{
void eat(){
System.out.println("Omnivores Eat Plants and meat");
}
}
class carnivores extends Animal{
void eat(){
System.out.println("Carnivores Eat meat");
}
}
class main{
public static void main(String args[]){
Animal A = new Animal();
Animal h = new herbivores(); //upcasting
Animal o = new omnivores(); //upcasting
Animal c = new carnivores(); //upcasting
A.eat();
h.eat();
o.eat();
c.eat();
}
}
Output:
Animals eat
Herbivores Eat Plants
Omnivores Eat Plants and meat
Carnivores eat meat
Example 2:
In this example, we are creating one superclass Hillstations and three subclasses Manali, Mussoorie, Gulmarg. Subclasses extend the superclass and override its location() and famousfor() method. We will call the location() and famousfor() method by the Parent class’, i.e. Hillstations class. As it refers to the base class object and the base class method overrides the superclass method; the base class method is invoked at runtime. Also, as Java Virtual Machine or the JVM and not the compiler determines method invocation, it is runtime polymorphism.
class Hillstations{
void location(){
System.out.println("Location is:");
}
void famousfor(){
System.out.println("Famous for:");
}
}
class Manali extends Hillstations {
void location(){
System.out.println("Manali is in Himachal Pradesh");
}
void famousfor(){
System.out.println("It is Famous for Hadimba Temple and adventure sports");
}
}
class Mussoorie extends Hillstations {
void location(){
System.out.println("Mussoorie is in Uttarakhand");
}
void famousfor(){
System.out.println("It is Famous for education institutions");
}
}
class Gulmarg extends Hillstations {
void location(){
System.out.println("Gulmarg is in J&K");
}
void famousfor(){
System.out.println("It is Famous for skiing");
}
}
class main{
public static void main(String args[]){
Hillstations A = new Hillstations();
Hillstations M = new Manali();
Hillstations Mu = new Mussoorie();
Hillstations G = new Gulmarg();
A.location();
A.famousfor();
M.location();
M.famousfor();
Mu.location();
Mu.famousfor();
G.location();
G.famousfor();
}
}
Output:
Location is:
Famous for:
Manali is in Himachal Pradesh
It is Famous for Hadimba Temple and adventure sports
Mussoorie is in Uttarakhand
It is Famous for education institutions
Gulmarg is in J&K
It is Famous for skiing
Example of run-time polymorphism in java
We will create two classes Car and Innova, Innova class will extend the car class and will override its run() method.
class Car
{
void run()
{
System.out.println(“ running”);
}
}
class innova extends Car
{
void run();
{
System.out.println(“ running fast at 120km”);
}
public static void main(String args[])
{
Car c = new innova();
c.run();
}
}
The output of the following program will be;
Running fast at 120 km.
Another example for run-time polymorphism in Java
Now, let us check if we can achieve runtime polymorphism via data members.
class car
{
int speedlimit = 125;
}
class innova extends car
{
int speedlimit = 135;
public static void main(String args[])
{
car obj = new innova();
System.out.println(obj.speedlimit);
}
The output of the following program will be :
125
This clearly implies we can’t achieve Runtime polymorphism via data members. In short, a method is overridden, not the data members.
Runtime polymorphism with multilevel inheritance
class grandfather
{
void swim()
{
System.out.println(“ Swimming”);
}
}
class father extends grandfather
{
void swim()
{
System.out.println(“ Swimming in river”);
}
}
class son extends father
{
void swim()
{
System.out.println(“ Swimming in pool”);
}
public static void main(String args[])
{
grandfather f1,f2,f3;
f1 =new grandfather();
f2 = new father();
f3 = new son();
f1.swim();
f2.swim();
f3.swim():
}
}
The output of the following program will be:
Swimming, Swimming in river, Swimming in pool
Another runtime polymorphism with multilevel inheritance example
class soundAnimal
{
public void Sound()
{
System.out.println("Different sounds of animal"); }
}
class buffalo extends soundAnimal
{
public void Sound()
{
System.out.println("The buffalo sound- gho,gho"); }
}
class snake extends soundAnimal
{
public void Sound()
{
System.out.println("The snake sound- his,his"); }
}
class tiger extends soundAnimal
{
public void Sound()
{
System.out.println("The tiger sounds- roooo, rooo"); }
}
public class Animal Main
{
public static void main(String[] args)
{
soundAnimal Animal = new soundAnimal(); soundAnimal buffalo = new buffalo();
soundAnimal snake = new snake();
soundAnimal tiger = new tiger();
Animal.Sound();
buffalo.Sound();
snake.Sound();
tiger.Sound();
}
}
The output of the following program will be;
The buffalo sound- gho,gho
The snake sound- his,his
The tiger sound- roooo,roooo
We hope you got an idea about runtime and compile-time polymorphism.
Polymorphic Subtypes
Subtype basically means that a subtype can serve as another type’s subtype, sounds a bit complicated?
Let’s understand this with the help of an example:
Assuming we have to draw some arbitrary shapes, we can introduce a class named ‘shape’ with a draw() method. By overriding draw() with other subclasses such as circle, square, rectangle, trapezium, etc we will introduce an array of type ‘shape’ whose elements store references will refer to ‘shape’ subclass references. Next time, we will call draw(), all shapes instances draw () method will be called.
This Subtype polymorphism generally relies on upcasting and late binding. A casting where you cast up the inheritance hierarchy from subtype to a supertype is termed upcasting.
To call non-final instance methods we use late binding. In short, a compiler should not perform any argument checks, type checks, method calls, etc, and leave everything on the runtime.
What is Polymorphism in Programming?
Polymorphism in programming is defined usage of a single symbol to represent multiple different types.
What is Polymorphism Variables?
A polymorphic variable is defined as a variable that can hold values of different types during the course of execution.
Why use Polymorphism in Java?
Polymorphism in Java makes it possible to write a method that can correctly process lots of different types of functionalities that have the same name. We can also gain consistency in our code by using polymorphism.
Advantages of Polymorphism in Java
- It provides reusability to the code. The classes that are written, tested and implemented can be reused multiple times. Furthermore, it saves a lot of time for the coder. Also, the one can change the code without affecting the original code.
- A single variable can be used to store multiple data values. The value of a variable you inherit from the superclass into the subclass can be changed without changing that variable’s value in the superclass; or any other subclasses.
- With lesser lines of code, it becomes easier for the programmer to debug the code.
Characteristics of Polymorphism
Polymorphism has many other characteristics other than Method Overloading and Method Overriding. They include:
- Coercion
- Internal Operator Overloading
- Polymorphic Variables or Parameters
1. Coercion
Coercion deals with implicitly converting one type of object into a new object of a different kind. Also, this is done automatically to prevent type errors in the code.
Programming languages such as C, java, etc support the conversion of value from one data type to another data type. Data type conversions are of two types, i.e., implicit and explicit.
Implicit type conversion is automatically done in the program and this type of conversion is also termed coercion.
For example, if an operand is an integer and another one is in float, the compiler implicitly converts the integer into float value to avoid type error.
Example:
class coercion {
public static void main(String[] args) {
Double area = 3.14*5*7;
System.out.println(area);
String s = "happy";
int x=5;
String word = s+x;
System.out.println(word);
}
}
Output:
109.9
happy5
2. Internal Operator Overloading
In Operator Overloading, an operator or symbol behaves in more ways than one depending upon the input context or the type of operands. It is a characteristic of static polymorphism. Although Java does not support user-defined operator overloading like C++, where the user can define how an operator works for different operands, there are few instances where Java internally overloads operators.
Operator overloading is the concept of using the operator as per your choice. Therefore, an operator symbol or method name can be used as a ‘user-defined’ type as per the requirements.
For example, ‘+’ can be used to perform the addition of numbers (same data type) or for concatenation of two or more strings.
In the case of +, can be used for addition and also for concatenation.
For example:
class coercion {
public static void main(String[] args) {
String s = "happy";
String s1 = "world";
int x=5;
int y=10;
System.out.println(s+s1);
System.out.println(x+y);
}
}
Output :
Similarly, operators like! &, and | are also in the overload position for logical and bitwise operations. In both of these cases, the type of argument will decide how the operator will interpret.
3. Polymorphic Variables or Parameters
In Java, the object or instance variables represent the polymorphic variables. This is because any object variables of a class can have an IS-A relationship with their own classes and subclasses.
The Polymorphic Variable is a variable that can hold values of different types during the time of execution.
Parametric polymorphism specifies that while class declaration, a field name can associate with different types, and a method name can associate with different parameters and return types.
For example:
class Shape
{
public void display()
{
System.out.println("A Shape.");
}
}
class Triangle extends Shape
{
public void display()
{
System.out.println("I am a triangle.");
}
}
class Main{
public static void main(String[] args)
{
Shape obj;
obj = new Shape();
obj.display();
obj = new Triangle();
obj.display();
}
}
Output:
A Shape.
I am a triangle.
Here, the obj object is a polymorphic variable. This is because the superclass’s same object refers to the parent class (Shape) and the child class (Triangle).
Problems with Polymorphism
With lots of advantages, there are also a few disadvantages of polymorphism.
- Polymorphism is quite challenging while implementation.
- It tends to reduce the readability of the code.
- It raises some serious performance issues in real-time as well.
Type Identification During Downcasting
Downcasting is termed as casting to a child type or casting a common type to an individual type.
So, we use downcasting whenever we need to access or understand the behaviour of the subtypes.
Example,
This is a hierarchical example
Food> Vegetable> Ladyfinger, Tomato
Here, tomato and ladyfinger are two subclasses.
In downcasting, we narrow the type of objects, which means we are converting common type to individual type.
Vegetable vegetable = new Tomato();
Tomato castedTomato = (Tomato) vegetable;
Here we are casting common type to an individual type, superclass to subclass which is not possible directly in java.
We explicitly tell the compiler what the runtime type of the object is.
Fragile base class problem
Fragile base class problem is nothing but a fundamental architectural problem.
Sometimes the improper design of a parent class can lead a subclass of a superclass to use superclass in some unpredicted ways.
The fragility of inheritance will lead to broken codes even when all the criteria is met.
This architectural problem is termed as a fragile base class problem in object-oriented programming systems and language.
Basically, the reason for the fragile base problem is that the developer of the base class has no idea of the subclass design. There is no solution yet for this problem.
Conclusion
We hope you must have got a basic idea of polymorphism in Java and how we use it as well as problems related to them.
Hence, this brings us to the end of the blog on Polymorphism in Java. Furthermore, to learn more about programming and other related concepts, check out the courses on Great Learning Academy and PG Programs in Software Engineering.
Also, if you are preparing for Interviews, check out these Interview Questions for Java to ace it like a pro.
So, don’t stop your journey of learning. Also, don’t forget to upskill and reskill yourself. Keep exploring and keep learning.
Frequently Asked Questions
One of the OOPs features that allows us to carry out a single action in various ways is known as polymorphism in Java. For example, we have a class Animal with a method sound(). This is a generic class and so we cannot give it an implementation such as: Meow, Oink, Roar, etc.
The four types of polymorphism are:
– Runtime or Subtype polymorphism
– Overloading or Parametric polymorphism
– Compile-time or Ad hoc polymorphism
– Casting or Coercion polymorphism
One of the core concepts of OOP or object-oriented programming, polymorphism describes situations in which a particualr thing occurs in different forms. In computer science, polymorphism describes a concept that allows us to access different types of objects through the same interface.
In object-oriented programming, overriding is a feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes.
If two or more methods in the same class have the same name, but have different parameters, this is known as Overloading. In case of Overriding, a method signature (name and parameters) are found in the same superclass and the child class.
Engaging in the study of Java programming suggests a keen interest in the realm of software development. For those embarking upon this journey with aspirations towards a career in this field, it is recommended to explore the following pages in order to acquire a comprehensive understanding of the development career path: