Memra

Polymorphism & dynamic method binding

Late binding, overriding, and how calling through a supertype dispatches to the actual subclass method.

The same call, different behavior

Polymorphism means one reference type can produce many behaviors at runtime. When you call an overridden method through a superclass reference, Java looks at the actual object's class — not the reference's declared type — to decide which method body runs. This decision is made at *run time*, which is why it is called late binding (or dynamic dispatch). Instance methods in Java are late-bound by default.

Worked example: Shape.draw() overridden

class Shape {
    void draw() { System.out.println("Shape"); }
}

class Circle extends Shape {
    @Override void draw() { System.out.println("Circle"); }
}

class Square extends Shape {
    @Override void draw() { System.out.println("Square"); }
}

Now drive them through an array of the supertype:

Shape[] shapes = { new Circle(), new Square(), new Shape() };
for (Shape sh : shapes) {
    sh.draw();
}

This prints:

Circle
Square
Shape

Even though every element is *declared* Shape, each sh.draw() dispatches to the overriding method of the real object. The loop never mentions Circle or Square — yet it does the right thing for each. That is the payoff: you can add a Triangle extends Shape later, drop it into the array, and this loop calls Triangle.draw() with no change to this code. Code written against the supertype stays open to new subtypes — the extensibility that polymorphism buys.

@Override is your safety net

The @Override annotation tells the compiler "I intend to override a superclass method." If the signature doesn't actually match one (a typo, a wrong parameter type), the compiler errors instead of silently creating a brand-new, never-called method. Always write it.

NORMAL ~/memra/learn/comp-308/polymorphism-and-dynamic-binding utf-8 LF