Modern Java Evolution

Pattern Matching & Switch Expressions

The pattern-matching trilogy — instanceof patterns (16), switch expressions (14), and patterns in switch with record deconstruction (21) — turns type-test-and-cast chains into checked, exhaustive, declarative code.
  • if (o instanceof String s) — test, cast, and bind in one step
  • Switch expressions yield values; arrow arms don't fall through
  • Type patterns in switch: case Integer i ->; guards: case String s when s.length() > 3 ->
  • Record patterns deconstruct: case Point(int x, int y) -> — nested patterns too
  • Sealed hierarchies + no default = compiler-checked exhaustiveness
  • case null is now expressible; without it, switch still NPEs on null selectors
From instanceof chains to deconstruction
sealed interface Shape permits Circle, Rect, Compound {}
record Circle(Point c, double r) implements Shape {}
record Rect(Point tl, Point br) implements Shape {}
record Compound(List<Shape> parts) implements Shape {}

double area(Shape s) {
    return switch (s) {
        case Circle(Point ignored, double r) -> Math.PI * r * r;
        case Rect(Point(var x1, var y1), Point(var x2, var y2)) ->   // nested!
                Math.abs((x2 - x1) * (y2 - y1));
        case Compound(List<Shape> parts) ->
                parts.stream().mapToDouble(this::area).sum();
    };  // no default: the compiler PROVES all Shapes are covered
}

The exhaustiveness guarantee is the headline: add a Triangle to the sealed interface and every switch over Shape fails to compile until it handles the new case — the compiler enforcing what the visitor pattern used to enforce with ceremony. This is the "data-oriented programming" style (Records + sealed + patterns): model data as closed hierarchies of transparent values, and process it with switch.

Guards and dominance
String describe(Object o) {
    return switch (o) {
        case null -> "nothing";                          // explicit null handling
        case Integer i when i < 0 -> "negative " + i;     // guarded pattern
        case Integer i -> "int " + i;                     // must come AFTER the guarded one
        case String s -> "text \"" + s + "\"";
        default -> o.toString();
    };
}
Sources
  • Core Java, Volume I: Fundamentals (13th ed.)Ch. 5.9 — Pattern Matching
  • Learning Java (6th ed.)Ch. 4–5 — Language; Objects