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 nullis now expressible; without it, switch still NPEs on null selectors
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.
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();
};
}