Card 07/ 09

ComparisonDifficulty: Advanced2 min

Polymorphic Methods vs instanceof Chains

Put the behaviour on the types or in the caller, decided by which changes more often.

Behaviour that varies by type can live on the types, as an overridden method, or in the caller, as a chain of instanceof checks or a switch. Methods on the types make new types cheap; the chain in the caller makes new operations cheap.

Two ways to vary behaviour by type, on what each change costs
Compared onA method on each typeA chain of instanceof in the caller
Adding a new typeOne new class; nothing else is touchedEvery chain must be found and extended
Adding a new operationEvery class must gain a methodOne new method in one place
Missing a caseThe compiler refuses the classFalls through the chain, silently
Where the behaviour livesBeside the data it usesAway from it, in the caller

The decision rule. When new types arrive more often than new operations (payment methods, shapes, report formats), put the method on the type. When the types are fixed and closed and the operations keep coming, the chain in the caller is the honest shape.

Java 21 makes the second case safe rather than merely tolerable. A switch with type patterns over a sealed type, one that lists every class allowed to extend it, is checked by the compiler for completeness, so the silent fall-through in row three stops being possible.

java
String describe(Shape s) {
    return switch (s) {
        case Circle c -> "circle of " + c.radius();
        case Square q -> "square of " + q.side();
        case Line l   -> "line";
    };
}
no default, and it compiles only because Shape is sealed over exactly these three

What has not changed is the first row. Adding a fourth shape still means finding this method, and the compiler will now make sure you do. A build that refuses is a much better failure than a cast, the bracketed type that forces a conversion, that throws on one record in production.