⚡ Play a quiz
HomeLearnCS CoreOOPs Concepts › Polymorphism in practice
Lesson 3 of 3 · OOPs Concepts

Runtime Polymorphism, Virtual Methods and Dynamic Dispatch

OOPs Concepts100%

Polymorphism is the pillar with the most machinery behind it. Knowing how the call is resolved — and what it costs — separates a memorised definition from an answer that holds up under follow-up questions.

Read time
7 min
Track
CS Core
Sections
3
Practice
0

How the call is resolved

When a method can be overridden, the compiler cannot know at compile time which implementation will run — the variable's declared type says Notification, but the object might be an SmsNotification. So the decision is deferred to runtime: each class has a table of method pointers (a vtable), each object knows its class, and the call is one extra indirection through that table.

  • Java: every non-final, non-private, non-static method is virtual by default.
  • C++: only methods marked virtual are dispatched dynamically; the rest are resolved at compile time.
  • Python and JavaScript: methods are looked up on the object at call time, so everything is effectively virtual.
note

Is it slow?

One pointer indirection, and the JIT often removes it entirely by inlining when only one implementation is ever loaded. Dispatch cost is almost never the reason a program is slow — do not design around it.

Notification n declared type n.send(…) points at SmsNotification actual object knows its class CLASS METHOD TABLE send → SMS gateway this runs one indirection Add a channel → add a class. The calling code never changes.
The declared type decides what you may call; the actual object decides what runs. That one indirection is dynamic dispatch.

Abstract class or interface?

Java, from version 8 onwards
Abstract classInterface
Instance fieldsYesNo (constants only)
ConstructorYesNo
Method bodiesYesYes, via default
How many per classOneMany
Use whenSubclasses share state and behaviourYou are defining a capability

The practical rule: reach for an interface first, because a class can implement several and it keeps the inheritance slot free. Move to an abstract class only when there is genuine shared state to hold.

Replacing conditional chains

The most valuable everyday use of polymorphism is deleting switch statements that grow every time a case is added.

Before: every new type edits this method
double area(Shape s) {
    if (s.type == CIRCLE)    return Math.PI * s.r * s.r;
    if (s.type == SQUARE)    return s.side * s.side;
    if (s.type == RECTANGLE) return s.w * s.h;
    throw new IllegalArgumentException("unknown shape");
}
After: a new type is a new class, and nothing else changes
interface Shape { double area(); }

record Circle(double r)              implements Shape { public double area() { return Math.PI * r * r; } }
record Square(double side)           implements Shape { public double area() { return side * side; } }
record Rectangle(double w, double h) implements Shape { public double area() { return w * h; } }

This is the open/closed principle in one diff: the system is open to new shapes and closed to modification of existing code. The unreachable "unknown shape" branch disappears too, because the type system now guarantees it.

Key takeaways

  • Dynamic dispatch defers the choice of implementation to runtime via a vtable lookup.
  • Java methods are virtual by default; in C++ you opt in with the virtual keyword.
  • Prefer interfaces; use an abstract class when subclasses genuinely share state.
  • Replacing a type switch with polymorphism is the open/closed principle in practice.

Frequently asked questions

What is dynamic dispatch?

The mechanism that picks a method implementation at runtime based on the actual type of the object, rather than the declared type of the variable, usually through a per-class table of method pointers.

What is the difference between an abstract class and an interface?

An abstract class can hold instance state, a constructor and shared implementation, and a class can extend only one. An interface defines a capability, holds no instance state, and a class can implement many.

Can constructors be polymorphic?

No. Constructors are not inherited and are not dispatched dynamically. A factory method or the factory pattern is the usual way to choose which type to create at runtime.

Test yourself on OOPs Concepts

Reading is not recall. Take a timed quiz on this topic solo, or share a room code and battle friends on it.

⚡ Start the OOPs Concepts quiz

More in OOPs Concepts