🎭 Polymorphism in Java — Dynamic Dispatch and Upcasting
Master polymorphism in Java — runtime vs compile-time polymorphism, dynamic method dispatch, upcasting/downcasting, virtual methods and the role of @Override. With examples.
Polymorphism = 'many forms'. One reference type, many runtime implementations. It's what makes OOP flexible.
🔀 Two kinds
- Compile-time (static) — method overloading; the compiler picks the method by argument types.
- Runtime (dynamic) — method overriding; the JVM picks the method by the object's actual type.
⚙️ Dynamic method dispatch
A superclass reference can point to a subclass object. When you call an overridden method, the JVM dispatches to the actual object's implementation, not the reference type's:
Animal a = new Dog(); // upcasting
a.speak(); // calls Dog.speak() at runtimeIn Java, all non-static, non-final, non-private methods are 'virtual' — overridable and dynamically dispatched by default.
⬆️⬇️ Upcasting & downcasting
- Upcast (subclass → superclass) — always safe, implicit.
- Downcast (superclass → subclass) — explicit, can throw
ClassCastException; guard withinstanceof.
💡 The power
Write code against the abstraction (Animal, List, Shape) and it works with any current or future implementation. This is the basis of clean, extensible design.
💻 Code Examples
Dynamic dispatch through a superclass reference
class Shape { double area() { return 0; } }
class Circle extends Shape {
double r;
Circle(double r){ this.r = r; }
@Override double area() { return Math.PI * r * r; }
}
Shape s = new Circle(2); // upcast
System.out.printf("%.2f%n", s.area()); // Circle.area()12.57
Polymorphic loop over a list
List<Shape> shapes = List.of(new Circle(1), new Circle(2));
double total = 0;
for (Shape sh : shapes) total += sh.area();
System.out.printf("%.2f%n", total);15.71
Safe downcast with instanceof
Shape s = new Circle(3);
if (s instanceof Circle c) {
System.out.println(c.r);
}3.0
⚠️ Common Mistakes
- Downcasting without an instanceof check — throws ClassCastException at runtime.
- Expecting field access to be polymorphic — only methods dispatch dynamically; fields are resolved by reference type.
- Marking a method final or private and expecting it to be overridable — those aren't virtual.
- Confusing overloading (compile-time) with overriding (runtime) polymorphism.
🎯 Interview Questions
Real questions asked at top product and service-based companies.
Q1.What is polymorphism?Beginner
Q2.What is dynamic method dispatch?Intermediate
Q3.Are Java fields polymorphic?Intermediate
Q4.What's the difference between upcasting and downcasting?Advanced
Q5.Which methods are NOT polymorphic in Java?Advanced
🧠 Quick Summary
- Polymorphism = one reference type, many runtime forms.
- Overloading = compile-time; overriding = runtime polymorphism.
- Dynamic dispatch picks the method by the object's actual type.
- Fields are NOT polymorphic; static/final/private aren't virtual.
- Upcast is safe and implicit; downcast needs instanceof.