Pattern matching for instanceof (finalized in Java 16) lets a type-check bind the checked value directly to a new, correctly-typed variable in the same expression, removing the previously-required separate manual cast. Pattern matching for switch (finalized in Java 21) extends this to switch statements and expressions, allowing cases to match by type rather than only exact value. Record patterns (also finalized in Java 21) go further, destructuring a record's components directly inside the pattern itself.
Pattern matching for instanceof: no separate cast (Java 16+)
Before Java 16, this required a type check followed by a completely separate, redundant manual cast; now the check and the cast happen together in one expression.
Object obj = "hello";
// BEFORE Java 16:
if (obj instanceof String) {
String s = (String) obj; // separate, redundant manual cast
System.out.println(s.length());
}
// Java 16+: pattern matching binds s directly
if (obj instanceof String s) {
System.out.println(s.length()); // s is already typed String, no cast needed
}Pattern matching for switch: matching by type (Java 21+)
A switch can now branch by the checked value's runtime type, not just by an exact value — each case both checks and binds in one line, similar in spirit to Rust or Kotlin's when-is patterns.
Object obj = 42;
String description = switch (obj) {
case Integer i -> "an integer: " + i;
case String s -> "a string of length " + s.length();
case null -> "it was null";
default -> "something else";
};Record patterns: destructuring in one step (Java 21+)
The record's own components are extracted directly inside the pattern — no separate .x() and .y() calls needed afterward.
record Point(int x, int y) { }
Object obj = new Point(3, 4);
if (obj instanceof Point(int x, int y)) { // destructures x and y directly
System.out.println(x + y); // 7 — no p.x(), p.y() calls needed
}