Memra

Polymorphism & dynamic method binding

◈ 5 cards

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.

extendsextendsextendsShapedraw() prints ShapeCircledraw() prints CircleSquaredraw() prints SquareTriangleadded later
One method name, four bodies. Triangle is greyed because it does not exist yet — the point of writing the loop against Shape is that adding it later needs no change to the loop.
declared type of shobject it points tosh.draw() printsShapenew Circle()CircleShapenew Square()SquareShapenew Shape()ShapeOne call site, three outcomes — chosen at run time.
The exam question in one table. The left column never changes and the right column always does: the declared type decides what you may CALL, the runtime object decides what RUNS.
NORMAL ~/memra/learn/comp-308/polymorphism-and-dynamic-binding utf-8 LF