Polymorphism In Java: Meaning, Advantages, & More
Did you know polymorphism allows programmers to perform a single action in various ways? It is a critical feature of the Java language and one of the fundamental concepts of object-oriented programming. It offers great flexibility and extensibility in the Java code.
In this blog, we will explore the polymorphism definition in Java with examples, its types, and its major advantages and disadvantages.
What is Polymorphism?
Polymorphism is derived from two Greek words, “poly” and “morph”, which mean “many” and “forms”, respectively. Hence, polymorphism meaning in Java refers to the ability of objects to take on many forms. In other words, it allows different objects to respond to the same message or method call in multiple ways.
Polymorphism allows coders to write code that can work with objects of multiple classes in a generic way without knowing the specific class of each object. So, now that you can define polymorphism in Java, let’s understand its concept with some examples.
Polymorphism in Java Example
As previously explained, polymorphism in Java helps an object take on many different forms. In this section, we will provide different examples of polymorphism to show how it works.
Java Runtime Polymorphism Example: Animal Sounds
In this example, the Animal class has a makeSound() method that outputs “Animal making a sound…” while the subclasses Dog, Cat, and Elephant, each provide their own implementation of the same function to produce individual noises.
class Animal {
void makeSound() {
System.out.println("Animal making a sound...");
}
}
class Dog extends Animal {
void makeSound() {
System.out.println("Dog barking...");
}
}
class Cat extends Animal {
void makeSound() {
System.out.println("Cat meowing...");
}
}
class Elephant extends Animal {
void makeSound() {
System.out.println("Elephant trumpeting...");
}
}
class TestPolymorphism2 {
public static void main(String args[]) {
Animal animal;
animal = new Dog();
animal.makeSound();
animal = new Cat();
animal.makeSound();
animal = new Elephant();
animal.makeSound();
}
}
Output:
Dog barking… Cat meowing… Elephant trumpeting… |
Java Runtime Polymorphism Example: Fruit Tastes
In this example, Fruit is a class with the taste() method that prints “Fruit tasting…”. Its three subclasses, Apple, Orange, and Mango, each create their own implementation of the taste() method to reflect their unique tastes.
class Fruit {
void taste() {
System.out.println("Fruit tasting...");
}
}
class Apple extends Fruit {
void taste() {
System.out.println("Apple tasting sweet and crunchy...");
}
}
class Orange extends Fruit {
void taste() {
System.out.println("Orange tasting tangy and juicy...");
}
}
class Mango extends Fruit {
void taste() {
System.out.println("Mango tasting tropical and delicious...");
}
}
class TestPolymorphism2 {
public static void main(String args[]) {
Fruit fruit;
fruit = new Apple();
fruit.taste();
fruit = new Orange();
fruit.taste();
fruit = new Mango();
fruit.taste();
}
}
Output:
Apple tasting sweet and crunchy… Orange tasting tangy and juicy… Mango tasting tropical and delicious… |
Types of Polymorphism
There are two main types of polymorphism in Java: compile-time and runtime.
Compile-Time Polymorphism
Compile-time polymorphism is also called static polymorphism. It allows users to define multiple methods with the same name but different parameters. It is beneficial because it helps write cleaner and more efficient code.
The two types of compile-time polymorphism are:
- Method Overloading: In method overloading, the methods with the same name can have different numbers of parameters, different types of parameters, or different order of parameters in methods.
Example:
Here is an example of method overloading in Java.
class MathOperation {
// Method with one parameter
public int multiply(int a, int b) {
return a * b;
}
// Method with the same name but two parameters
public int multiply(int a, int b, int c) {
return a * b * c;
}
// Method with the same name but different types of parameters
public double multiply(double a, double b) {
return a * b;
}
}
public class Main {
public static void main(String[] args) {
MathOperation op = new MathOperation();
System.out.println(op.multiply(4, 5)); // Calls the first method
System.out.println(op.multiply(4, 5, 6)); // Calls the second method
System.out.println(op.multiply(4.5, 5.5)); // Calls the third method
}
}
In the above example, the ‘MathOperation’ class has three methods named ‘multiply’. The first method multiplies two integers, the second multiplies three integers, and the third multiplies two double values. When ‘multiply’ is called from the ‘main’ method, the suitable version of ‘multiply’ based on the number and types of arguments used is processed.
Operator Overloading: It is the process of providing multiple meanings to an operator. Java offers limited support for in-built operator overloading. The ‘+’ operator is used for addition of numbers and concatenation of strings. It is important to note that user-defined operator overloading is not supported.
Example:
Here is an example of operator overloading in Java.
public class OperatorOverloadingExample {
public static void main(String[] args) {
// Operator '+' used for addition
int a = 10;
int b = 20;
int sum = a + b;
System.out.println("Sum of a and b is: " + sum);
// Operator '+' used for String concatenation
String firstName = "Java";
String lastName = "Programming";
String fullName = firstName + " " + lastName;
System.out.println("Full Name: " + fullName);
}
}
In the above example, when the ‘+’ operator is used with integers (a+b), it performs arithmetic addition. When the same operator is used with the strings (firstName + ” ” + lastName), it concatenates them.
Runtime Polymorphism
Runtime polymorphism is also known as dynamic method dispatch. This process handles the call to an overridden method dynamically during runtime. You can learn more about the principles of polymorphism by taking this in-depth Java course. The following is a type of runtime polymorphism in Java.
Method Overriding: It is a type of runtime polymorphism where the method in the subclass overrides the method in the superclass, which has the same name, parameters, and return type. Here, the method to be called is based on the instance of the subclass, which is assigned to a reference variable of the superclass.
Example:
Here is an example of method overriding in Java:
class Vehicle {
// Method in the superclass
void move() {
System.out.println("Vehicle is moving");
}
}
class Car extends Vehicle {
// Overriding method in the subclass
@Override
void move() {
System.out.println("Car is driving");
}
}
class Bicycle extends Vehicle {
// Overriding method in the subclass
@Override
void move() {
System.out.println("Bicycle is cycling");
}
}
public class Main {
public static void main(String[] args) {
Vehicle myVehicle = new Vehicle(); // Create a Vehicle object
Vehicle myCar = new Car(); // Create a Car object
Vehicle myBicycle = new Bicycle(); // Create a Bicycle object
myVehicle.move(); // Output: Vehicle is moving
myCar.move(); // Output: Car is driving
myBicycle.move(); // Output: Bicycle is cycling
}
}
In the above example, there is a superclass ‘Vehicle’ and two subclasses ‘Car’ and ‘Bicycle’. The superclass has the method ‘move()’ and the subclasses override the method ‘move()’ from the superclass ‘Vehicle’. Now, if the ‘move()’ method is called on an object of type ‘Car’ or ‘Bicycle’, Java uses the overridden method ‘move()’ from the respective subclass and not from ‘Vehicle’.
Difference Between Compile-time and Run-time Polymorphism in Java
Compile-time and runtime polymorphism, both, come with their unique set of features. Here’s a table that summarizes the key differences between runtime and compile time polymorphism in Java.
Feature | Compile-time Polymorphism | Run time Polymorphism |
---|---|---|
Also known as | Method overloading | Method overriding |
Method signature | Must differ in number or types of parameters | Must have the same name, number, and types of parameters as the method it overrides |
Example | Multiple methods with the same name but different parameters | A subclass providing its own implementation of a method that is already defined in its superclass |
Benefit | Allows multiple methods with the same name for related functionality | Provides more flexibility and dynamic behavior based on the actual type of the object at runtime |
Example of Compile-Time Polymorphism
The following is an example of the compile-time polymorphism in Java:
class StringOperations { // Method to concatenate two strings static String concatenate(String a, String b) { return a + b; } // Overloaded method to concatenate three strings static String concatenate(String a, String b, String c) { return a + b + c; } public static void main(String[] args) { System.out.println(concatenate(“Hello, “, “World!”)); // Calls first method System.out.println(concatenate(“Java “, “is “, “fun!”)); // Calls overloaded method }} |
Example of Run-Time Polymorphism
The following is an example of run-time polymorphism:
class Shape {
String draw() {
return "Drawing a shape";
}
}
class Circle extends Shape {
@Override
String draw() {
return "Drawing a circle";
}
}
class Square extends Shape {
@Override
String draw() {
return "Drawing a square";
}
}
public class ShapeDemo {
public static void main(String[] args) {
Shape myShape = new Circle(); // At runtime, Circle's draw() is called
System.out.println(myShape.draw());
myShape = new Square(); // At runtime, Square's draw() is called
System.out.println(myShape.draw());
}
}
In the compile-time polymorphism example, the compiler decides which ‘concatenate’ method to call based on the number of string arguments. In the run-time polymorphism example, the object type (circle or square) determines which ‘draw’ method is called at runtime.
Characteristics of Polymorphism
Some important characteristics of polymorphism are as follows:
- It allows the same name for variables and methods with different data types. These variables are known as polymorphic variables or parameters.
- The behavior of a method depends on the data provided.
- It supports implicit type conversion to prevent type errors during compilation time. Implicit type conversion is when the compiler automatically converts one data type to another without loss of data.
- The functionality of a method changes according to different scenarios.
Need help understanding the basics of Java? Check out this guide on Java basics and learn about its methods, data types, and more.
Advantages of Polymorphism
Polymorphism provides many advantages that can improve the readability, maintainability, and efficiency of code, such as:
- Code Reusability: Polymorphism provides the reuse of code, as methods with the same name can be used in different classes.
- Flexibility and Dynamism: Polymorphism allows for more flexible and dynamic code, where the behaviour of an object can change at runtime depending on the context in which it is being used.
- Reduced Complexity: It can reduce the complexity of code by allowing the use of the same method name for related functionality, making the code easier to read and maintain.
- Simplified Coding: Polymorphism simplifies coding by reducing the number of methods and constructors that need to be written.
- Better Organization: Polymorphism allows for better organization of code by grouping related functionality in one class.
- Code Extensibility: Polymorphism enables code extensibility, as new subclasses can be created to extend the functionality of the superclass without modifying the existing code.
- Increased Efficiency: Compile-time polymorphism can lead to more efficient coding. The compiler can choose the appropriate method to call at compile time, based on the number, types, and order of arguments passed to it.
Also Read: Inheritance in Java With Examples
Disadvantages of Polymorphism in Java
Here are some potential disadvantages of polymorphism:
- Complexity: It can sometimes make code more complex, especially when multiple methods or constructors with the same name are defined in a class hierarchy.
- Debugging: Debugging polymorphic code can be more challenging, as it may be difficult to trace the exact method that is being executed at runtime.
- Performance Overhead: Runtime polymorphism can introduce a performance overhead, as the system needs to determine the method to execute based on the actual type of the object at runtime.
- Overuse: Overuse of polymorphism can lead to excessive abstraction and unnecessary complexity in the code.
- Name Ambiguity: The use of the same name for different methods or constructors can lead to name ambiguity, making it harder to understand the code.
- Inflexibility: Compile-time polymorphism can be inflexible, as the method to execute is determined at compile-time based on the number, types, and order of arguments passed to it.
Conclusion
Polymorphism in Java is a powerful feature for writing efficient and flexible code. By understanding the principles of polymorphism and using them effectively in Java, you can create more scalable and robust applications. The two types of polymorphism, runtime and compile-time, can help you to define one interface with multiple implementations. It is beneficial when you want a single action to be performed in different ways suitable for diverse scenarios.
Have you written code using Java before? Share with us in the comments section below how you utilized polymorphism in your code. Also, check out different types of polymorphism in Java for further understanding.
FAQs
Polymorphism in Java is accomplished through two distinct methods: method overloading and method overriding. It can be divided into two different categories – compile-time polymorphism, which is achieved by way of method overloading; and runtime polymorphism, which takes place via the process of overriding.
When a child class has the same method signature as its superclass, this is known as overriding. On the other hand, if two or more methods in one single class have the same name but different parameters, it is called overloading.
Polymorphism in Java provides us with the capability to take advantage of inherited properties and utilize them for carrying out a variety of tasks. This provides us with multiple options on how we can achieve the same outcome.
We can take the example of shapes to explain polymorphism. Let us say there is a superclass ‘Shape’ with a method ‘draw()’ and subclasses ‘triangle’, ‘circle’, and ‘square’ with their ‘draw()’ methods. Due to polymorphism, Java will determine the actual type of object (triangle, circle, or square) and call the appropriate ‘draw()’ method during runtime despite the variable type being ‘Shape’.
Polymorphism is a concept where a function is defined in more than one form whereas in inheritance, a new class inherits the features from an already existing class. The concept of polymorphism applies to functions or methods whereas inheritance applies to classes.
Yes, polymorphism is possible without inheritance. You can use interface implementation and method overloading to achieve polymorphism. Interface implementation allows different classes to use the same interface but implement methods differently. Method overloading allows the same method name to perform different functions based on parameters. Both processes do not use inheritance, which is a subclass inheriting the methods and properties of a parent class.