Polymorphism
Polymorphism means “many forms.” In Java, it allows the same method name or interface to behave differently based on context. Polymorphism improves flexibility, extensibility, and maintainability and is central to OOP design.
Java supports two types of polymorphism:
- Compile-time Polymorphism (Static Binding)
- Runtime Polymorphism (Dynamic Binding)
What Is Polymorphism?
- One interface, multiple implementations
- Same method name, different behavior
- Enables loose coupling and extensibility
1️⃣ Compile-time Polymorphism (Static Polymorphism)
Compile-time polymorphism is achieved using method overloading. The method call is resolved at compile time based on the method signature (name + parameters).
Key Characteristics
- Achieved using method overloading
- Resolved at compile time
- Faster execution
- No inheritance required
Example: Method Overloading
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;
}
}
Why compile-time? The compiler decides which add() method to call based on arguments.
Compile-time Polymorphism Rules
- Same method name
- Different parameter list
- Return type alone is not enough
- Binding happens before execution
2️⃣ Runtime Polymorphism (Dynamic Polymorphism)
Runtime polymorphism is achieved using method overriding and inheritance. The method call is resolved at runtime based on the actual object, not the reference type.
Key Characteristics
- Achieved using inheritance + overriding
- Resolved at runtime
- Requires IS-A relationship
- Supports dynamic behavior
Example: Method Overriding
class Animal {
void sound() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
Animal a = new Dog();
a.sound(); // Dog barks
- Reference type → Animal
- Object type → Dog
- Method executed → Dog’s implementation
Why Runtime Polymorphism Works
- JVM uses Dynamic Method Dispatch
- Method resolution depends on object type at runtime
- Enables plug-and-play behavior
Compile-time vs Runtime Polymorphism (Interview Favorite)
| Aspect | Compile-time | Runtime |
|---|---|---|
| Also called | Static Polymorphism | Dynamic Polymorphism |
| Achieved by | Method Overloading | Method Overriding |
| Binding time | Compile time | Runtime |
| Inheritance required | ❌ No | ✅ Yes |
| Method signature | Different | Same |
| Performance | Faster | Slightly slower |
| Flexibility | Limited | High |
Polymorphism with Parent Reference
Parent p = new Child();
- Access decided by reference type
- Method execution decided by object type
Polymorphism Limitations
- Static methods → Not overridden (method hiding)
- Final methods → Cannot be overridden
- Private methods → Not overridden
- Variables → No polymorphism (resolved by reference type)
class A {
int x = 10;
}
class B extends A {
int x = 20;
}
A obj = new B();
System.out.println(obj.x); // 10
Real-World Example (Very Interview-Friendly)
class Payment {
void pay() {
System.out.println("Generic payment");
}
}
class CreditCard extends Payment {
void pay() {
System.out.println("Credit card payment");
}
}
class UPI extends Payment {
void pay() {
System.out.println("UPI payment");
}
}
Payment p = new CreditCard();
p.pay(); // Credit card payment
- Easy to add new payment types
- No code change in existing logic
Benefits of Polymorphism
- Loose coupling
- Easy extensibility
- Cleaner code
- Better abstraction
- Supports Open-Closed Principle
Common Beginner Mistakes
- Confusing overloading with overriding
- Expecting variable polymorphism
- Overriding static methods
- Forgetting inheritance
- Not using @Override
Interview-Ready Answers
Short Answer
Polymorphism allows the same method to behave differently based on context.
Detailed Answer
In Java, polymorphism is achieved in two ways: compile-time polymorphism using method overloading, where method resolution happens at compile time, and runtime polymorphism using method overriding, where the JVM determines the method call at runtime based on the object type.
Key Takeaway
Compile-time polymorphism decides behavior early. Runtime polymorphism decides behavior dynamically. Polymorphism is the heart of flexible and extensible Java design and is essential for frameworks, APIs, and real-world applications.