Polymorphism & Dynamic Dispatch
Polymorphism & Dynamic Dispatch
You have learned how to create child classes and override methods. Now comes the idea that ties all of it together: polymorphism — the ability of one variable to behave like objects of many different types. Combined with dynamic dispatch, which is how Java decides at runtime which version of an overridden method to call, polymorphism becomes the backbone of flexible, extensible design.
A Parent Reference to a Child Object
In Java, a variable of a parent type can hold a reference to any child object. This is perfectly legal because a child is-a parent:
The variable a is declared as Animal, but it is pointing at a Dog object. When speak() is called, Java does not use the Animal version — it uses the actual object's version. That choice happens at runtime, not at compile time. This is dynamic dispatch.
Why Does This Matter? Programming to a Type
The real power shows up when you write code that works with the parent type and therefore works with any present or future child. This principle is called programming to a type (sometimes phrased as "program to an interface, not an implementation").
ZooKeeper.makeAnimalSpeak was written once and never needs to change, even if you add a Lion class tomorrow. The new class simply overrides speak(), and dynamic dispatch handles the rest.
Polymorphism with Arrays and Lists
Storing mixed child objects under a common parent type in a collection is a classic pattern:
The loop does not need to know or care about the concrete type. The output is:
Animal a = new Dog() over Dog a = new Dog() when the rest of the code only needs Animal behaviour. This lets you swap the concrete type later without touching every line that uses a.
What Dynamic Dispatch Does NOT Cover
Dynamic dispatch applies to instance methods only. It does not apply to:
- Fields — if a parent and child both declare a field with the same name, the reference type determines which field is read, not the object type.
- Static methods — static methods belong to the class, not to objects, so they are resolved at compile time based on the reference type.
A Concrete Example: Shape Area
Here is a self-contained example you can compile and run, showing exactly how dynamic dispatch selects the correct area() at runtime:
printArea is written against Shape. It will work correctly for every Shape subclass ever created, without any changes.
Summary
- A parent-type variable can hold a reference to any child object.
- Dynamic dispatch ensures the overridden method of the actual object is called at runtime.
- Programming to a type means writing code against a parent type so it works with all children — past and future.
- Dynamic dispatch applies to instance methods only, not to fields or static methods.