Polymorphism: One Reference, Many Behaviors
Introduction
Polymorphism means “many forms.” In Java, a parent reference can point to child objects, and overridden methods run based on the actual object type at runtime. This chapter focuses on overriding, upcasting, downcasting, and instanceof—complementing overloading from Methods.
Prerequisites
Two Kinds of Polymorphism (Recap)
| Kind | Mechanism | When resolved |
|---|---|---|
| Overloading | same name, different parameters | compile time |
| Overriding | same signature in child | runtime |
Animal animal = new Cat();
animal.speak(); // Cat's speak() if overridden1) Upcasting (Implicit)
Assign child reference to parent type—always safe.
Cat cat = new Cat();
Animal animal = cat; // upcast to Animal
Notifier notifier = new EmailNotifier(); // interface referenceYou lose child-only methods on the variable type:
animal.speak(); // OK
// animal.meow(); // compile error — Animal has no meow()2) Dynamic Method Dispatch
JVM calls the actual object’s overridden method:
public class Animal {
public void speak() { System.out.println("..."); }
}
public class Dog extends Animal {
@Override public void speak() { System.out.println("Woof"); }
}
public class Cat extends Animal {
@Override public void speak() { System.out.println("Meow"); }
public void meow() { System.out.println("Purrr"); }
}
public static void announce(Animal animal) {
animal.speak(); // Woof or Meow depending on object
}
announce(new Dog());
announce(new Cat());This is how frameworks process List<Animal> with mixed concrete types.
3) Downcasting (Explicit)
When you need child-specific API:
Animal animal = new Cat();
if (animal instanceof Cat cat) { // pattern matching Java 16+
cat.meow();
}Classic form:
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.meow();
}Warning
Wrong cast → ClassCastException. Check with instanceof first.
4) instanceof Patterns
Modern Java:
if (shape instanceof Circle circle) {
System.out.println(circle.radius());
}Scope of circle is the if block.
5) Polymorphism with Interfaces
void notifyAll(Notifier[] notifiers, String msg) {
for (Notifier n : notifiers) {
n.send(msg); // each implementation behaves differently
}
}Same idea as abstract parent references—prefer interfaces for flexible APIs.
6) Life Metaphor
Remote control button labeled “Play” (interface method).
DVD player plays movie; speaker plays music—same button, different device behavior.
Common Beginner Mistakes
Confusing Overload with Override at Runtime
Overload choice is fixed at compile time based on reference type for instance methods... actually instance overload uses compile-time reference type; override uses runtime object. Keep concepts separate.
Downcasting Without Check
Always guard with instanceof.
Calling Overridden Method in Constructor
Child fields may not be initialized—risky design.
Mini Practice
Shape[] shapes = { new Circle(2), new Rectangle(3, 4) }; — loop and print each area() using Shape reference.
What’s Next
Metadata on code: Annotations, then Functional Programming.
FAQ
Why use parent type references?
Write one method process(Animal a) for all animals—extensible when new animals arrive.
Does polymorphism work with static methods?
No—static methods are not overridden. Use instance methods for polymorphic behavior.
Can primitives be polymorphic?
No. Autoboxing wraps primitives in objects when needed (Integer).
How does polymorphism help testing?
Mock implementations of interfaces replace real services in tests.
Relation to @Override?
Annotation marks intent; JVM still uses virtual method table for dispatch.