Chapter 14 · Week 14

Polymorphism in Java

"For just as the body is one and has many members, and all the members of the body, though many, are one body, so it is with Christ." — 1 Corinthians 12:12
14

Polymorphism with cleaner defaults

Build a polymorphic ArrayList; press run; watch the enhanced-for dispatch to the right override on each element. Java made the safe defaults explicit.

Java Polymorphism Playground

Build an ArrayList<Vehicle> by adding cars, trucks, and motorcycles. Press "run loop" — the enhanced-for dispatches describe() to the right override on each element. No virtual keyword needed; in Java every method is dispatched dynamically by default.

Java vs C++: In C++ you'd write virtual void describe() in the base and override in the derived. Java makes both implicit — every method is virtual by default, and @Override is an optional safety annotation. The mechanism is the same; Java just picked safer defaults.

Why This Matters

A short chapter. The reason: you've already learned the ideas. Chapter 12 covered inheritance, virtual methods, polymorphic collections. The Java version is simpler than the C++ version — Java made better defaults.

What Java Does for You

C++Java
: public Vehicleextends Vehicle
Vehicle(w, m) in init listsuper(w, m);
virtual on base methodnothing — all methods virtual by default
override on derived@Override (annotation)
virtual ~Class()(no destructor needed)
Vehicle* fleet[N]Vehicle[] fleet or ArrayList<Vehicle>
slicing is a hazardno slicing — refs only

All Methods Virtual by Default

You don't write virtual in Java. Every non-static method dispatches dynamically. The mechanism is the same as C++ — Java just turned it on universally. Cleaner default.

@Override — Cheap Insurance

public class Car extends Vehicle {
    @Override
    public void describe() { /* ... */ }
}

@Override is technically optional. Always write it. If you typo the method name, the compiler refuses to compile — saving you from the silent "polymorphism doesn't work" bug.

No Slicing

In C++, void f(Vehicle v) slices off the derived parts when you pass a Car. In Java, Vehicle is a reference type — passing it just passes the reference, so the dynamic type is preserved. This whole class of bug goes away.

instanceof and Casting

for (Vehicle v : fleet) {
    if (v instanceof Car c) {
        System.out.println("car passengers: " + c.getPassengers());
    }
}

Java 16+ has pattern-matching instanceof — checks the type and casts in one step. Use sparingly. Heavy instanceof usually means a virtual method is missing — refactor to add one.

Coach's Note — Java is, somewhat infamously, a language where inheritance gets overused. When we move through the standard library you'll see 4- and 5-level hierarchies that any modern engineer would refactor. Watch for it. Just because the language makes inheritance easy doesn't mean every problem wants it.

No Project This Week

Use the week to consolidate. Catch up on your Project 13 migration. Read Chapter 15's content (abstracts and interfaces) — both will be needed for the final.

Check Your Reps

Java Polymorphism — Quick Check

Question 1 of 4
In Java, which is required to enable polymorphic dispatch on a method?
Why: Java has no virtual keyword because all non-static methods are virtual by default. @Override is recommended (catches typos at compile time) but not strictly required. The dispatch happens regardless of whether you mark the override.
Question 2 of 4
What's the issue here?
public class Car extends Vehicle {
    public void descrobe() {  // ← typo, no @Override
        // ...
    }
}
Why: Without @Override, the compiler is happy to let you add a new method called descrobe. When you call vehicle.describe(), the base version runs — your "override" is never reached. Adding @Override would have caught the typo at compile time. Always write @Override.
Question 3 of 4
You pass a Car by value to a method expecting Vehicle in Java. What happens?
Why: Java has no slicing. Object-typed variables are references, so passing a Car as a Vehicle parameter just passes the reference — the dynamic type is still Car. This is one of Java's wins over C++ for OO safety.
Question 4 of 4
You find yourself writing lots of if (v instanceof Car) ... else if (v instanceof Truck) .... What does that usually indicate?
Why: Heavy instanceof chains are a smell: they're switch-on-type code that could be replaced by polymorphism. Add a virtual method on the base (e.g., describe() or getCapacity()) that each subclass overrides; replace the chain with one call. The compiler dispatches for you.
YOU FINISHED. NICE WORK.

← WEEK 13: HELLO JAVA   ·   WEEK 15: ABSTRACTS →