Card 07/ 09
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.
| Compared on | A method on each type | A chain of instanceof in the caller |
|---|---|---|
| Adding a new type | One new class; nothing else is touched | Every chain must be found and extended |
| Adding a new operation | Every class must gain a method | One new method in one place |
| Missing a case | The compiler refuses the class | Falls through the chain, silently |
| Where the behaviour lives | Beside the data it uses | Away 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.
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";
};
}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.
← → move through sections and cards