What is polymorphism in Java?
Polymorphism, one of the core principles of Object-Oriented Programming (OOP) in Java, allows objects to take on many forms. It enables a single interface to represent different underlying forms or types, promoting flexibility and reusability in code.
What is Polymorphism?
Polymorphism literally means 'many forms'. In Java, it refers to the ability of an object to take on many forms. Specifically, it allows you to define one interface and have multiple implementations. This concept is fundamental to achieving flexibility and extensibility in software design.
Types of Polymorphism
Compile-Time Polymorphism (Static Polymorphism)
Compile-time polymorphism is achieved through method overloading. Method overloading occurs when a class has multiple methods with the same name but different parameters (number of parameters, type of parameters, or order of parameters). The Java compiler determines which overloaded method to call at compile time based on the method signature.
class Calculator {
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
double add(double a, double b) {
return a + b;
}
}
public class Demo {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(5, 10)); // Calls int add(int, int)
System.out.println(calc.add(5, 10, 15)); // Calls int add(int, int, int)
System.out.println(calc.add(5.5, 10.5)); // Calls double add(double, double)
}
}
- Achieved by method overloading.
- Methods have the same name but different parameter lists.
- Decision of which method to call is made at compile time.
- Increases the readability and flexibility of the program.
Run-Time Polymorphism (Dynamic Polymorphism)
Run-time polymorphism is achieved through method overriding. Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass. The decision of which method to call is made at runtime by the Java Virtual Machine (JVM) based on the actual type of the object, not the reference type.
class Animal {
void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Cat meows");
}
}
public class Demo {
public static void main(String[] args) {
Animal myAnimal = new Animal(); // Animal reference and object
Animal myDog = new Dog(); // Animal reference but Dog object
Animal myCat = new Cat(); // Animal reference but Cat object
myAnimal.makeSound(); // Output: Animal makes a sound
myDog.makeSound(); // Output: Dog barks (Runtime polymorphism)
myCat.makeSound(); // Output: Cat meows (Runtime polymorphism)
}
}
- Achieved by method overriding.
- Methods have the same name, same parameters, and are in different classes (parent-child relationship).
- Decision of which method to call is made at runtime.
- Uses 'upcasting' (referring to a subclass object by a superclass reference).
Key Benefits of Polymorphism
- Code Reusability: Write code once and use it for different types of objects.
- Flexibility and Extensibility: Easily add new types without modifying existing code.
- Maintainability: Easier to manage and update code.
- Decoupling: Reduces dependencies between components, improving modularity.
- Dynamic Behavior: Allows objects to behave differently based on their actual type at runtime.
Summary Table: Compile-Time vs. Run-Time Polymorphism
| Feature | Compile-Time Polymorphism | Run-Time Polymorphism |
|---|---|---|
| Mechanism | Method Overloading | Method Overriding |
| Binding | Static Binding (Early Binding) | Dynamic Binding (Late Binding) |
| Decision Time | Compile Time | Run Time |
| Method Signature | Same name, different parameters | Same name, same parameters |
| Relationship | Within the same class | Between parent and child classes (inheritance) |
| Example | Overloaded `add()` methods | Overridden `makeSound()` method |