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)

KindMechanismWhen resolved
Overloadingsame name, different parameterscompile time
Overridingsame signature in childruntime
java
Animal animal = new Cat();
animal.speak();  // Cat's speak() if overridden

1) Upcasting (Implicit)

Assign child reference to parent type—always safe.

java
Cat cat = new Cat();
Animal animal = cat;      // upcast to Animal
Notifier notifier = new EmailNotifier();  // interface reference

You lose child-only methods on the variable type:

java
animal.speak();   // OK
// animal.meow();  // compile error — Animal has no meow()

2) Dynamic Method Dispatch

JVM calls the actual object’s overridden method:

java
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:

java
Animal animal = new Cat();
if (animal instanceof Cat cat) {   // pattern matching Java 16+
    cat.meow();
}

Classic form:

java
if (animal instanceof Cat) {
    Cat cat = (Cat) animal;
    cat.meow();
}

Warning

Wrong cast → ClassCastException. Check with instanceof first.

4) instanceof Patterns

Modern Java:

java
if (shape instanceof Circle circle) {
    System.out.println(circle.radius());
}

Scope of circle is the if block.

5) Polymorphism with Interfaces

java
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.