Polymorphism
Many forms of the same entity
Overview
Polymorphism means "many forms". It allows objects of different classes to be treated as objects of a common superclass. The same method call can behave differently based on the actual object type.
There are two types:
- Compile-time (Static): Method overloading
- Runtime (Dynamic): Method overriding
Key Concepts
Same interface, different implementations
Method Overloading: Same name, different parameters (compile-time)
Method Overriding: Child redefines parent method (runtime)
Enables writing flexible and extensible code
Works through inheritance and interfaces
Code Example
1// Interface defining common behavior
2interface Shape {
3 double calculateArea();
4 void draw();
5}
6
7// Different implementations
8class Circle implements Shape {
9 private double radius;
10
11 public Circle(double radius) {
12 this.radius = radius;
13 }
14
15 @Override
16 public double calculateArea() {
17 return Math.PI * radius * radius;
18 }
19
20 @Override
21 public void draw() {
22 System.out.println("Drawing a circle with radius " + radius);
23 }
24}
25
26class Rectangle implements Shape {
27 private double width, height;
28
29 public Rectangle(double width, double height) {
30 this.width = width;
31 this.height = height;
32 }
33
34 @Override
35 public double calculateArea() {
36 return width * height;
37 }
38
39 @Override
40 public void draw() {
41 System.out.println("Drawing rectangle " + width + "x" + height);
42 }
43}
44
45class Triangle implements Shape {
46 private double base, height;
47
48 public Triangle(double base, double height) {
49 this.base = base;
50 this.height = height;
51 }
52
53 @Override
54 public double calculateArea() {
55 return 0.5 * base * height;
56 }
57
58 @Override
59 public void draw() {
60 System.out.println("Drawing triangle with base " + base);
61 }
62}
63
64// Polymorphism in action
65public class ShapeCalculator {
66 // Works with ANY shape - polymorphic method
67 public static void printShapeInfo(Shape shape) {
68 shape.draw();
69 System.out.println("Area: " + shape.calculateArea());
70 }
71
72 public static double getTotalArea(List<Shape> shapes) {
73 double total = 0;
74 for (Shape shape : shapes) {
75 total += shape.calculateArea(); // Polymorphic call
76 }
77 return total;
78 }
79
80 public static void main(String[] args) {
81 List<Shape> shapes = new ArrayList<>();
82 shapes.add(new Circle(5));
83 shapes.add(new Rectangle(4, 6));
84 shapes.add(new Triangle(3, 4));
85
86 // Same method call, different behavior
87 for (Shape shape : shapes) {
88 printShapeInfo(shape);
89 }
90
91 System.out.println("Total area: " + getTotalArea(shapes));
92 }
93}printShapeInfo() works with any Shape. The actual method called depends on the runtime type of the object.
Advantages
- ✓
Flexible and extensible code
- ✓
Reduces code duplication
- ✓
Easy to add new types without changing existing code
- ✓
Enables loose coupling
- ✓
Simplifies complex conditional logic
Real-World Example
Media Player: play() method works differently for MP3, MP4, AVI files - same action, different behavior.
Payment System: pay() method works with CreditCard, DebitCard, UPI, Wallet - each processes differently.
Notification Service: send() method works for Email, SMS, Push - same interface, different delivery mechanisms.
Best Practices
- 1.
Use polymorphism to eliminate switch/if-else chains
- 2.
Program to interfaces for maximum flexibility
- 3.
Ensure proper method overriding with @Override annotation
- 4.
Design for extension using polymorphism
- 5.
Combine with Factory pattern for object creation
💡 Interview Tips
Explain compile-time vs runtime polymorphism
Give Shape or Animal example for clear demonstration
Show how it eliminates conditional logic
Discuss how it enables Open/Closed Principle
Mention role in Strategy and Factory patterns