Polymorphism & Dynamic Dispatch — One Name, Many Behaviours
Polymorphism ও Dynamic Dispatch — এক নাম, বহু আচরণ
1. One Call, Many Behaviours
Polymorphism — Greek for "many shapes" — is the OOP idea that one method call can invoke
different implementations depending on the actual runtime type of the object. The compiler sees a
Shape; the JVM dispatches to Circle.area() or Square.area() at run
time. This single mechanism lets you write code against an abstraction and have it automatically pick up
future subclasses — without modifying the caller.
Shape, কিন্তু JVM runtime-এ সিদ্ধান্ত নেয় Circle.area() না Square.area() চালাবে। এই একক কৌশলই abstraction-এর বিরুদ্ধে কোড লেখা সম্ভব করে — নতুন subclass এলেও caller পরিবর্তন করতে হয় না।
The runtime selection of the right overridden method is called dynamic method dispatch (or late binding). It is the engine behind every OO framework — Spring, Android, Swing — you will ever meet.
2. Method Override & @Override
Override means: in a subclass, redefine a method declared in the parent with the exact same signature.
Always mark overrides with the @Override annotation — the compiler will then catch typos or
signature mismatches for you.
@Override annotation দিন — একটু ভুল টাইপ হলেও compiler ধরিয়ে দেবে।
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; }
}
class Square extends Shape {
double s;
Square(double s) { this.s = s; }
@Override
double area() { return s * s; }
}
class Main {
public static void main(String[] args) {
Shape[] shapes = { new Circle(3), new Square(4) };
for (Shape sh : shapes) {
System.out.println("area = " + sh.area()); // dynamic dispatch
}
}
}
3. How the JVM Picks the Method
Two pieces of type information matter:
- Static type — the declared type of the reference (
Shape sh). The compiler uses it to check that the method exists. - Dynamic type — the actual class of the object on the heap (
CircleorSquare). The JVM uses it to pick which override to run.
4. Upcasting & Downcasting
Assigning a subclass object to a superclass reference is an upcast — always safe, and
usually implicit. Going the other way (downcast) is a promise to the compiler that the
object really is of that subtype at runtime. If you lie, the JVM throws ClassCastException.
class Animal { void speak() { System.out.println("..."); } }
class Dog extends Animal {
@Override void speak() { System.out.println("Ghew ghew!"); }
void fetch() { System.out.println("Fetching"); }
}
class Main {
public static void main(String[] args) {
Animal a = new Dog(); // upcast — implicit
a.speak(); // dispatches to Dog.speak()
// a.fetch(); // compile error — Animal has no fetch()
if (a instanceof Dog d) { // pattern-matching instanceof (Java 16+)
d.fetch(); // safe downcast, ready to use
}
}
}
instanceof (Java 16+) combines the type check and cast into
one statement — no more repeated (Dog) a inside the if-block. Cleaner, safer.
5. Polymorphism in Action — Heterogeneous Lists
The real pay-off of polymorphism is writing code against the abstraction. Here is a payroll system that
iterates over a list of Employees but correctly computes each subclass's salary:
import java.util.List;
class Employee {
String name;
Employee(String n) { name = n; }
double salary() { return 0; }
}
class Engineer extends Employee {
Engineer(String n) { super(n); }
@Override double salary() { return 80000; }
}
class Manager extends Employee {
Manager(String n) { super(n); }
@Override double salary() { return 120000; }
}
class Main {
public static void main(String[] args) {
List<Employee> team = List.of(
new Engineer("Rahim"),
new Manager("Shahida")
);
double total = 0;
for (Employee e : team) total += e.salary();
System.out.println("Monthly payroll: BDT " + total);
}
}
6. Vocabulary (শব্দভাণ্ডার)
| Term | Meaning | বাংলায় |
|---|---|---|
| Polymorphism | One method call, many possible implementations. | একই কল, বিভিন্ন রূপায়ন। |
| Override | Subclass redefines a parent's method. | Parent method পুনঃসংজ্ঞায়ন। |
@Override | Compiler-checked override marker. | Compiler-checked override চিহ্ন। |
| Dynamic dispatch | JVM picks the method at runtime by actual class. | Runtime-এ আসল class-এর উপর method বাছাই। |
| Upcast | Subclass ref → superclass ref (safe, implicit). | Subclass → superclass reference (নিরাপদ)। |
| Downcast | Superclass ref → subclass ref (needs runtime check). | Superclass → subclass reference (runtime check দরকার)। |
instanceof | Runtime type test; pattern-matching form since Java 16. | Runtime type পরীক্ষা; Java 16+ থেকে pattern-matching। |
7. Practice Problems
-
Create
Paymentwith methodprocess(); override it inBkashandNagad. Loop over an array ofPaymentand callprocess()on each.Payment.process()override করেBkashওNagadবানান। একটিPayment[]array loop করে process() ডাকুন।✨ Show Answer
Main.javaclass Payment { void process() { System.out.println("Generic payment"); } } class Bkash extends Payment { @Override void process() { System.out.println("Paying via bKash"); } } class Nagad extends Payment { @Override void process() { System.out.println("Paying via Nagad"); } } class Main { public static void main(String[] args) { Payment[] ps = { new Bkash(), new Nagad() }; for (Payment p : ps) p.process(); } } -
In 3 sentences, explain the difference between method overloading and method overriding.তিন বাক্যে — method overloading ও method overriding-এর পার্থক্য কী?
✨ Show Answer
Answer: Overloading is compile-time: the same class has multiple methods with the same name but different parameter lists, and the compiler picks one by static types. Overriding is runtime: a subclass redefines a parent method with the identical signature, and the JVM picks the right version based on the object's actual class (dynamic dispatch). Overloading is about having many flavours of a method; overriding is about changing behaviour for a subtype.
Overloading compile-time-এ হয় — একই name-এর একাধিক method ভিন্ন parameter list নিয়ে, compiler static type দেখে বাছে। Overriding runtime-এ হয় — subclass একই signature-এ parent-এর method পুনঃসংজ্ঞায়িত করে, JVM আসল class দেখে বাছে।
-
Write a program that prints
trueif anObject ois either aStringorInteger. Use pattern-matchinginstanceof.Pattern-matchinginstanceofব্যবহার করে এমন কোড লিখুন যাObject oString বা Integer হলে true প্রিন্ট করবে।✨ Show Answer
Main.javaclass Main { static boolean isTextOrInt(Object o) { return (o instanceof String s && !s.isEmpty()) || (o instanceof Integer i && i > 0); } public static void main(String[] args) { System.out.println(isTextOrInt("Dhaka")); System.out.println(isTextOrInt(42)); System.out.println(isTextOrInt(3.14)); } } -
A static method in a parent is "hidden" rather than overridden. What does that mean? Illustrate briefly.Parent-এর static method subclass-এ override হয় না, "hide" হয় — এর মানে কী? ছোট উদাহরণ দিন।
✨ Show Answer
Answer: Static methods belong to the class, not to an instance, so they do not participate in dynamic dispatch. If a subclass declares a static method with the same signature, it hides the parent's, and which one you call depends on the static type of the reference — not the object.
Main.javaclass P { static void hi() { System.out.println("P.hi"); } } class C extends P { static void hi() { System.out.println("C.hi"); } } class Main { public static void main(String[] args) { P p = new C(); p.hi(); // prints P.hi — no dynamic dispatch for static } } -
Why does marking a method
finaldisable polymorphism for it? Answer in 1–2 sentences.Method-কেfinalকরলে সেটি polymorphic থাকে না কেন — ১-২ বাক্যে।✨ Show Answer
Answer:
finalexplicitly tells the compiler "no subclass may override this method," so there is nothing for the JVM to dispatch between — the call is fixed at compile time. It is a tool for safety (preserve invariants) and sometimes performance (JIT can inline aggressively).finalবললেই subclass override করতে পারে না, তাই JVM-এর dispatch করার কিছু নেই — compile-time-এই fix। নিরাপত্তা ও কখনো performance-এর জন্য কাজে আসে।
Summary — Module 15
Polymorphism means one call can run many implementations, selected at run time by the
object's actual class — this is dynamic dispatch. To use it, a subclass
overrides a parent method with the identical signature and the @Override annotation.
Upcasting a subclass to its parent type is always safe; downcasting is not, and should be guarded by
instanceof (preferably pattern-matching style from Java 16+). Static methods, private methods,
and final methods are not polymorphic — they are bound at compile time.
@Override দেয়। Upcast সবসময় নিরাপদ; downcast-এ instanceof (Java 16+-এ pattern-matching) ব্যবহার করুন। Static, private ও final method polymorphic নয় — compile-time-এ bound।