Casting and instanceof Patterns

OOP · lesson 21 of 43 · 3 min read

Pattern matching for instanceof, and why a plain cast can still blow up at runtime.

Open this lesson in the learning hub

Key points

  • Upcasting to a supertype is always safe and needs no syntax. Downcasting is a claim the compiler cannot check.
  • A wrong downcast throws ClassCastException at runtime, usually far away from the code that caused it.
  • if (o instanceof Click c) tests and binds in one step, so there is no separate cast to get wrong.
  • The bound variable is in scope exactly where the test is known to be true, including after &&.
  • Tests run top to bottom and the first match wins, so put the narrower condition above the broader one.
  • A long instanceof ladder is often polymorphism waiting to happen, or a sealed type plus a switch.

Example

import java.util.List;

public class Main {
    interface Event { }
    record Click(int x, int y) implements Event { }
    record Scroll(int amount)  implements Event { }
    record Key(char c)         implements Event { }

    // Each test runs in order, and the first match wins.
    static String handle(Object o) {
        if (o instanceof Click c && c.x() > 100) return "far click at x=" + c.x();
        if (o instanceof Click c)                return "click at x=" + c.x();
        if (o instanceof Scroll s)               return "scroll " + s.amount();
        if (o instanceof String s)               return "text of length " + s.length();
        return "unhandled: " + o.getClass().getSimpleName();
    }

    public static void main(String[] args) {
        List<Object> inbox = List.of(new Click(120, 40), new Click(10, 10),
                                     new Scroll(-3), "hello", new Key('k'));
        for (Object o : inbox) {
            System.out.println(handle(o));
        }

        Object sneaky = "not an event";
        try {
            Event bad = (Event) sneaky;      // compiles, because the static type is Object
            System.out.println(bad);
        } catch (ClassCastException e) {
            System.out.println("ClassCastException: " + e.getMessage());
        }
    }
}

Let instanceof bind the variable for you, and reorder tests so the narrow one is first.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the OOP course, and every lesson in it is listed on the OOP contents page.