Add patterns module: pattern matching from JDK 21 to 27

Type, record and primitive patterns, guards, sealed exhaustiveness, MatchException via separate compilation, and eight captured transcripts.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01TF9JWFvJSNm6HVzswzZU5a
This commit is contained in:
Claude
2026-09-24 11:50:40 +00:00
parent 96b25c8fc4
commit bc91b9c8d0
36 changed files with 736 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
public class Guards {
static String size(Object o) {
return switch (o) {
case String s when s.isEmpty() -> "empty string";
case String s when s.length() < 5 -> "short string: " + s;
case String s -> "long string: " + s;
case Integer i when i < 0 -> "negative " + i;
case Integer i -> "int " + i;
default -> "other";
};
}
public static void main(String[] args) {
for (Object o : new Object[]{"", "abc", "abcdefgh", -4, 12, 1.5}) System.out.println(size(o));
}
}
+15
View File
@@ -0,0 +1,15 @@
public class MatchExceptionDemo {
record Fragile(int v) {
public int v() { throw new IllegalStateException("accessor blew up"); }
}
public static void main(String[] args) {
Object o = new Fragile(1);
try {
if (o instanceof Fragile(int v)) System.out.println("matched " + v);
} catch (MatchException e) {
System.out.println("MatchException: " + e.getMessage());
System.out.println("cause: " + e.getCause());
}
}
}
+31
View File
@@ -0,0 +1,31 @@
public class NullCase {
static String withoutCaseNull(Object o) {
return switch (o) {
case String s -> "string";
default -> "other";
};
}
static String withCaseNull(Object o) {
return switch (o) {
case null -> "null!";
case String s -> "string";
default -> "other";
};
}
static String combined(Object o) {
return switch (o) {
case String s -> "string";
case null, default -> "null or something else";
};
}
public static void main(String[] args) {
try { System.out.println(withoutCaseNull(null)); }
catch (NullPointerException e) { System.out.println("without case null: NullPointerException"); }
System.out.println("with case null: " + withCaseNull(null));
System.out.println("case null, default: " + combined(null));
System.out.println("instanceof with null: " + (null instanceof String));
}
}
+39
View File
@@ -0,0 +1,39 @@
public class RecordPatterns {
record Point(int x, int y) {}
record Line(Point from, Point to) {}
record Box<T>(T content) {}
// Deconstruct a record: the components are bound to x and y.
static String where(Object o) {
if (o instanceof Point(int x, int y)) {
return "point at " + x + "," + y;
}
return "not a point";
}
// Patterns nest: take a Line apart into its four coordinates in one step.
static int length1(Line line) {
if (line instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
return Math.abs(x2 - x1) + Math.abs(y2 - y1);
}
return -1;
}
// A nested pattern can also test the component's type.
static String box(Box<Object> b) {
return switch (b) {
case Box<Object>(String s) -> "box of string " + s;
case Box<Object>(Integer i) -> "box of int " + i;
case Box<Object>(var other) -> "box of " + other;
};
}
public static void main(String[] args) {
System.out.println(where(new Point(3, 4)));
System.out.println(where("nope"));
System.out.println(length1(new Line(new Point(0, 0), new Point(3, 4))));
System.out.println(box(new Box<>("hi")));
System.out.println(box(new Box<>(7)));
System.out.println(box(new Box<>(2.5)));
}
}
+21
View File
@@ -0,0 +1,21 @@
public class SealedExhaustive {
sealed interface Shape permits Circle, Square, Rect {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
record Rect(double w, double h) implements Shape {}
// No default: the compiler checks that every permitted subtype is covered.
static double area(Shape s) {
return switch (s) {
case Circle(double r) -> Math.PI * r * r;
case Square(double side) -> side * side;
case Rect(double w, double h) -> w * h;
};
}
public static void main(String[] args) {
for (Shape s : new Shape[]{new Circle(1), new Square(2), new Rect(2, 3)}) {
System.out.printf("%s -> %.2f%n", s, area(s));
}
}
}
+45
View File
@@ -0,0 +1,45 @@
public class TypePatterns {
// The old way: test, cast, then use.
static String oldStyle(Object o) {
if (o instanceof String) {
String s = (String) o;
return "string of length " + s.length();
}
return "something else";
}
// The pattern way: test and bind in one step.
static String newStyle(Object o) {
if (o instanceof String s) {
return "string of length " + s.length();
}
return "something else";
}
// The binding variable stays in scope wherever the test is known to have succeeded.
static int lengthOrMinusOne(Object o) {
if (!(o instanceof String s)) {
return -1;
}
return s.length(); // s is in scope here: we only get here if the test succeeded
}
// A switch can test many types in a row.
static String describe(Object o) {
return switch (o) {
case Integer i -> "int " + (i + 1);
case String s -> "string " + s.toUpperCase();
case int[] arr -> "int array of " + arr.length;
default -> "other " + o.getClass().getSimpleName();
};
}
public static void main(String[] args) {
System.out.println(oldStyle("hello") + " / " + newStyle("hello"));
System.out.println(lengthOrMinusOne("abc") + " " + lengthOrMinusOne(42));
System.out.println(describe(41));
System.out.println(describe("java"));
System.out.println(describe(new int[]{1, 2, 3}));
System.out.println(describe(3.5));
}
}
+23
View File
@@ -0,0 +1,23 @@
public class Unnamed {
record Point(int x, int y) {}
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
static String kind(Shape s) {
return switch (s) {
case Circle _ -> "round"; // the type matters, the value does not
case Square _ -> "angular";
};
}
static int xOnly(Object o) {
if (o instanceof Point(int x, _)) return x; // do not care about y
return -1;
}
public static void main(String[] args) {
System.out.println(kind(new Circle(1)) + " " + kind(new Square(1)));
System.out.println(xOnly(new Point(5, 9)));
}
}