Polymorphism & Dynamic Dispatch — One Name, Many Behaviours

Polymorphism ও Dynamic Dispatch — এক নাম, বহু আচরণ

Read: ~32 min Advanced 5 practice problems Live code runner

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.

Polymorphism মানে "বহু রূপ" — একই method কল বিভিন্ন subclass-এর আলাদা আচরণ চালাতে পারে। Compiler দেখে 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 মানে subclass-এ parent-এর একই signature-এর method পুনঃসংজ্ঞায়িত করা। সবসময় @Override annotation দিন — একটু ভুল টাইপ হলেও compiler ধরিয়ে দেবে।
Main.java
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:

  1. Static type — the declared type of the reference (Shape sh). The compiler uses it to check that the method exists.
  2. Dynamic type — the actual class of the object on the heap (Circle or Square). The JVM uses it to pick which override to run.
Compiler দেখে reference-এর declared type — যাচাই করে method আছে কিনা। JVM runtime-এ দেখে heap-এ থাকা আসল object-এর class — তার উপর ভিত্তি করে সঠিক override চালায়। এটাই dynamic dispatch।
sh.area() → static type: Shape, dynamic type: Circle Compiler "Does Shape have area()? Yes." ✔ JVM (runtime) "Heap object is Circle." Call Circle.area() returns πr² Compiler validates; JVM dispatches; override wins. Figure 15.1 — Dynamic dispatch = compile-time check + runtime selection।

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.

Main.java
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
        }
    }
}
Pattern-matching 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:

Main.java
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 (শব্দভাণ্ডার)

TermMeaningবাংলায়
PolymorphismOne method call, many possible implementations.একই কল, বিভিন্ন রূপায়ন।
OverrideSubclass redefines a parent's method.Parent method পুনঃসংজ্ঞায়ন।
@OverrideCompiler-checked override marker.Compiler-checked override চিহ্ন।
Dynamic dispatchJVM picks the method at runtime by actual class.Runtime-এ আসল class-এর উপর method বাছাই।
UpcastSubclass ref → superclass ref (safe, implicit).Subclass → superclass reference (নিরাপদ)।
DowncastSuperclass ref → subclass ref (needs runtime check).Superclass → subclass reference (runtime check দরকার)।
instanceofRuntime type test; pattern-matching form since Java 16.Runtime type পরীক্ষা; Java 16+ থেকে pattern-matching।

7. Practice Problems

  1. Create Payment with method process(); override it in Bkash and Nagad. Loop over an array of Payment and call process() on each.
    Payment.process() override করে Bkash ও Nagad বানান। একটি Payment[] array loop করে process() ডাকুন।
    ✨ Show Answer
    Main.java
    class 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();
        }
    }
  2. 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 দেখে বাছে।

  3. Write a program that prints true if an Object o is either a String or Integer. Use pattern-matching instanceof.
    Pattern-matching instanceof ব্যবহার করে এমন কোড লিখুন যা Object o String বা Integer হলে true প্রিন্ট করবে।
    ✨ Show Answer
    Main.java
    class 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));
        }
    }
  4. 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.java
    class 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
        }
    }
  5. Why does marking a method final disable polymorphism for it? Answer in 1–2 sentences.
    Method-কে final করলে সেটি polymorphic থাকে না কেন — ১-২ বাক্যে।
    ✨ Show Answer

    Answer: final explicitly 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.

Polymorphism মানে একই কল runtime-এ আসল object-এর class অনুসারে বিভিন্ন implementation চালায় — এটি dynamic dispatch। Subclass parent-এর একই signature-এ method override করে এবং @Override দেয়। Upcast সবসময় নিরাপদ; downcast-এ instanceof (Java 16+-এ pattern-matching) ব্যবহার করুন। Static, private ও final method polymorphic নয় — compile-time-এ bound।

Next Module → Abstract Classes & Interfaces — contract ছাড়া polymorphism অসম্পূর্ণ।