javabeginner

Pattern Matching for instanceof

Use modern Java pattern matching with instanceof, guarded patterns, and record deconstruction.

java
import java.util.*;

public class PatternMatchingDemo {

    // Sealed hierarchy
    sealed interface Shape permits Circle, Rectangle, Triangle {}
    record Circle(double radius) implements Shape {}
    record Rectangle(double width, double height) implements Shape {}
    record Triangle(double base, double height) implements Shape {}

    // Pattern matching with instanceof
    static double area(Shape shape) {
        if (shape instanceof Circle c) {
            return Math.PI * c.radius() * c.radius();
        } else if (shape instanceof Rectangle r) {
            return r.width() * r.height();
        } else if (shape instanceof Triangle t) {
            return 0.5 * t.base() * t.height();
        }
        throw new IllegalArgumentException("Unknown shape");
    }

    // Switch pattern matching (Java 21+)
    static String describe(Shape shape) {
        return switch (shape) {
            case Circle c when c.radius() > 10 -> "Large circle (r=" + c.radius() + ")";
            case Circle c    -> "Circle (r=" + c.radius() + ")";
            case Rectangle r -> "Rectangle (" + r.width() + "x" + r.height() + ")";
            case Triangle t  -> "Triangle (base=" + t.base() + ")";
        };
    }

    // Pattern matching with null
    static String format(Object obj) {
        return switch (obj) {
            case null          -> "null";
            case Integer i     -> "int: " + i;
            case String s      -> "str: \"" + s + "\"";
            case List<?> list  -> "list[" + list.size() + "]";
            case int[] arr     -> "array[" + arr.length + "]";
            default            -> obj.getClass().getSimpleName() + ": " + obj;
        };
    }

    // Real-world: JSON-like processing
    sealed interface JsonValue permits JsonString, JsonNumber, JsonArray, JsonObject {}
    record JsonString(String value) implements JsonValue {}
    record JsonNumber(double value) implements JsonValue {}
    record JsonArray(List<JsonValue> elements) implements JsonValue {}
    record JsonObject(Map<String, JsonValue> fields) implements JsonValue {}

    static String stringify(JsonValue value) {
        return switch (value) {
            case JsonString s  -> "\"" + s.value() + "\"";
            case JsonNumber n  -> String.valueOf(n.value());
            case JsonArray a   -> "[" + a.elements().stream()
                .map(PatternMatchingDemo::stringify)
                .reduce((x, y) -> x + "," + y)
                .orElse("") + "]";
            case JsonObject o  -> "{" + o.fields().entrySet().stream()
                .map(e -> "\"" + e.getKey() + "\":" + stringify(e.getValue()))
                .reduce((x, y) -> x + "," + y)
                .orElse("") + "}";
        };
    }

    public static void main(String[] args) {
        List<Shape> shapes = List.of(
            new Circle(5), new Rectangle(3, 4), new Triangle(6, 8));
        shapes.forEach(s -> System.out.printf("%s → area=%.2f%n", describe(s), area(s)));

        System.out.println(format(42));
        System.out.println(format("hello"));
        System.out.println(format(null));
    }
}

Use Cases

  • Type-safe polymorphic processing
  • Exhaustive switch over sealed types
  • ADT-style data modeling in Java

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.