Basics

12. Pattern Matching for instanceof

Pattern matching for instanceof (Java 16+) eliminates the redundant cast after a type check by introducing a binding variable that is already typed and in scope.

Java 16+ (standard)

Explanation

Before Java 16, a type check and cast required two steps: if (obj instanceof String) { String s = (String) obj; ... }. Pattern matching combines them: if (obj instanceof String s) { ... } โ€” s is automatically a String within the if block.

The binding variable is only in scope where the compiler can prove the pattern matched โ€” it is not in scope in the else branch. Negated patterns (!(obj instanceof String s)) make s available in the else/later code.

Pattern matching generalises in Java 21 to switch expressions (guarded patterns with when), record deconstruction patterns, and array patterns โ€” all built on this foundation.

Example

public class PatternMatchingInstanceof {
    sealed interface Animal permits Dog, Cat, Bird {}
    record Dog(String name) implements Animal {}
    record Cat(String name) implements Animal {}
    record Bird(String name, boolean canFly) implements Animal {}

    static String describe(Object obj) {
        // Old style (pre-Java 16): verbose and error-prone
        // if (obj instanceof String) {
        //     String s = (String) obj;  // redundant cast
        //     return "String: " + s.toUpperCase();
        // }

        // Pattern matching (Java 16+)
        if (obj instanceof String s) {
            return "String of length " + s.length() + ": " + s.toUpperCase();
        } else if (obj instanceof Integer i && i > 0) {
            // Guard condition in the same if
            return "Positive integer: " + i;
        } else if (obj instanceof Integer i) {
            return "Non-positive integer: " + i;
        }
        return "Unknown: " + obj;
    }

    static String describeAnimal(Animal a) {
        // Pattern matching switch (Java 21+)
        return switch (a) {
            case Dog d             -> d.name() + " says Woof";
            case Cat c             -> c.name() + " says Meow";
            case Bird b when b.canFly() -> b.name() + " can fly!";
            case Bird b            -> b.name() + " cannot fly";
        };
    }

    public static void main(String[] args) {
        System.out.println(describe("hello"));
        System.out.println(describe(42));
        System.out.println(describe(-5));
        System.out.println(describeAnimal(new Dog("Rex")));
        System.out.println(describeAnimal(new Bird("Penguin", false)));
    }
}

Sources