Dynamic dispatch in Java
The runtime object type decides which overridden method runs.
shape.area(). Sometimes it computes a circle, sometimes a square, sometimes a triangle. Same line ā the object decides. That's polymorphism.Same call, different behavior
When you call an instance method, the JVM picks the implementation from the object's actual runtime class, not from the variable's declared type. This is *dynamic dispatch*.
Animal a = new Dog();
a.speak(); // runs Dog's speak()A loop full of birds
What does this print?
class Bird { String move() { return "fly"; } }
class Penguin extends Bird {
String move() { return "waddle"; }
}
void main() {
Bird[] bs = { new Penguin(), new Bird() };
for (Bird b : bs)
System.out.print(b.move() + " ");
}fly flywaddle flywaddle waddle
Show the answer
waddle fly. Both elements are typed Bird, but each call runs the move() of the actual object.
Two questions, two answers
For a.speak(): (1) Does it compile? The compiler checks that the declared type has speak(). (2) Which body runs? At runtime, the JVM looks up the override in the object's class.
The declared type limits you
The object is a Dog, but the compiler only knows a is an Animal ā and Animal has no fetch(). Compile error, before anything runs.
class Animal { }
class Dog extends Animal {
void fetch() { }
}
Animal a = new Dog();
a.fetch(); // error!Calls inside the parent dispatch too
print() lives in Report. What does it show?
class Report {
String title() { return "Report"; }
void print() {
System.out.println(title() + "!");
}
}
class Sales extends Report {
String title() { return "Sales"; }
}
void main() { new Sales().print(); }Report!Sales!Compile error
Show the answer
Sales!. print() is inherited from Report, but its call to title() is still dispatched on the real object ā a Sales.
In real projects
Dynamic dispatch replaces long if (type == ...) chains: put shapes, payment methods or notification channels in one list and call the same method on each. Frameworks call your overrides this way ā you write run(), they call it.
Key takeaways
- Declared type decides what compiles
- Runtime class decides which override runs
- Calls inside the parent's own methods dispatch too
- Lets one loop handle many subtypes
The HotSpot JIT often makes polymorphic calls free: if only one implementation is actually used at a call site, it can inline that method directly.
Practice questions
What does this print?
class Animal { String speak() { return "?"; } }
class Dog extends Animal {
String speak() { return "Woof"; }
}
void main() {
Animal[] zoo = { new Dog(), new Animal() };
for (Animal a : zoo)
System.out.print(a.speak() + " ");
}- ? ?
- Woof ?
- Woof Woof
- Compile error
Check your answer
Woof ?. Both elements are typed Animal, but each call runs the speak() of the actual object: first the Dog's, then plain Animal's.
What does this print?
class A {
void run() { System.out.print(name()); }
String name() { return "A"; }
}
class B extends A {
String name() { return "B"; }
}
void main() { new B().run(); }- A
- B
- AB
Check your answer
B. run() is inherited from A, but its call to name() is still dispatched on the real object, which is a B.