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
+23
View File
@@ -0,0 +1,23 @@
$ javac src/TypePatterns.java && java TypePatterns (21.0.10+7-Ubuntu-124.04)
string of length 5 / string of length 5
3 -1
int 42
string JAVA
int array of 3
other Double
$ javac src/TypePatterns.java && java TypePatterns (25.0.4.1+1-LTS)
string of length 5 / string of length 5
3 -1
int 42
string JAVA
int array of 3
other Double
$ javac src/TypePatterns.java && java TypePatterns (27+35)
string of length 5 / string of length 5
3 -1
int 42
string JAVA
int array of 3
other Double
@@ -0,0 +1,39 @@
$ javac src/Guards.java && java Guards (25.0.4.1+1-LTS)
empty string
short string: abc
long string: abcdefgh
negative -4
int 12
other
$ javac src/Guards.java && java Guards (27+35)
empty string
short string: abc
long string: abcdefgh
negative -4
int 12
other
$ javac broken/Dominated.java (25.0.4.1+1-LTS)
broken/Dominated.java:5: error: this case label is dominated by a preceding case label
case String s -> "string"; // String is a CharSequence, so this can never be reached
^
1 error
$ javac broken/Dominated.java (27+35)
broken/Dominated.java:5: error: this case label is dominated by a preceding case label
case String s -> "string"; // String is a CharSequence, so this can never be reached
^
1 error
$ javac broken/GuardBeforeGeneral.java (25.0.4.1+1-LTS)
broken/GuardBeforeGeneral.java:5: error: this case label is dominated by a preceding case label
case String s when s.isEmpty() -> "empty string"; // guarded case after the unguarded one
^
1 error
$ javac broken/GuardBeforeGeneral.java (27+35)
broken/GuardBeforeGeneral.java:5: error: this case label is dominated by a preceding case label
case String s when s.isEmpty() -> "empty string"; // guarded case after the unguarded one
^
1 error
+31
View File
@@ -0,0 +1,31 @@
$ javac src/RecordPatterns.java && java RecordPatterns (21.0.10+7-Ubuntu-124.04)
point at 3,4
not a point
7
box of string hi
box of int 7
box of 2.5
$ javac src/RecordPatterns.java && java RecordPatterns (25.0.4.1+1-LTS)
point at 3,4
not a point
7
box of string hi
box of int 7
box of 2.5
$ javac src/RecordPatterns.java && java RecordPatterns (27+35)
point at 3,4
not a point
7
box of string hi
box of int 7
box of 2.5
$ javac src/MatchExceptionDemo.java && java MatchExceptionDemo (25.0.4.1+1-LTS)
MatchException: java.lang.IllegalStateException: accessor blew up
cause: java.lang.IllegalStateException: accessor blew up
$ javac src/MatchExceptionDemo.java && java MatchExceptionDemo (27+35)
MatchException: java.lang.IllegalStateException: accessor blew up
cause: java.lang.IllegalStateException: accessor blew up
+54
View File
@@ -0,0 +1,54 @@
$ javac src/SealedExhaustive.java && java SealedExhaustive (21.0.10+7-Ubuntu-124.04)
Circle[r=1.0] -> 3.14
Square[side=2.0] -> 4.00
Rect[w=2.0, h=3.0] -> 6.00
$ javac src/SealedExhaustive.java && java SealedExhaustive (25.0.4.1+1-LTS)
Circle[r=1.0] -> 3.14
Square[side=2.0] -> 4.00
Rect[w=2.0, h=3.0] -> 6.00
$ javac src/SealedExhaustive.java && java SealedExhaustive (27+35)
Circle[r=1.0] -> 3.14
Square[side=2.0] -> 4.00
Rect[w=2.0, h=3.0] -> 6.00
$ javac broken/NotExhaustive.java (21.0.10+7-Ubuntu-124.04)
broken/NotExhaustive.java:8: error: the switch expression does not cover all possible input values
return switch (s) {
^
1 error
$ javac broken/NotExhaustive.java (25.0.4.1+1-LTS)
broken/NotExhaustive.java:8: error: the switch expression does not cover all possible input values
return switch (s) {
^
1 error
$ javac broken/NotExhaustive.java (27+35)
broken/NotExhaustive.java:8: error: the switch expression does not cover all possible input values
return switch (s) {
^
missing patterns:
Rect _
1 error
$ javac broken/NotExhaustiveObject.java (21.0.10+7-Ubuntu-124.04)
broken/NotExhaustiveObject.java:3: error: the switch expression does not cover all possible input values
return switch (o) {
^
1 error
$ javac broken/NotExhaustiveObject.java (25.0.4.1+1-LTS)
broken/NotExhaustiveObject.java:3: error: the switch expression does not cover all possible input values
return switch (o) {
^
1 error
$ javac broken/NotExhaustiveObject.java (27+35)
broken/NotExhaustiveObject.java:3: error: the switch expression does not cover all possible input values
return switch (o) {
^
missing patterns:
Object _
1 error
@@ -0,0 +1,32 @@
$ java -cp old Client (Client and Shape compiled together, v1) (25.0.4.1+1-LTS)
circle: 3.141592653589793
square: 4.0
$ java -cp new:old Main (Shape gained Triangle; Client.class NOT recompiled) (25.0.4.1+1-LTS)
Exception in thread "main" java.lang.MatchException
at Client.area(Client.java:4)
at Main.main(Main.java:4)
$ javac -cp new sep/client/Client.java (now recompile Client against v2) (25.0.4.1+1-LTS)
sep/client/Client.java:4: error: the switch expression does not cover all possible input values
return switch (s) {
^
1 error
$ java -cp old Client (Client and Shape compiled together, v1) (27+35)
circle: 3.141592653589793
square: 4.0
$ java -cp new:old Main (Shape gained Triangle; Client.class NOT recompiled) (27+35)
Exception in thread "main" java.lang.MatchException
at Client.area(Client.java:4)
at Main.main(Main.java:4)
$ javac -cp new sep/client/Client.java (now recompile Client against v2) (27+35)
sep/client/Client.java:4: error: the switch expression does not cover all possible input values
return switch (s) {
^
missing patterns:
Triangle _
1 error
+26
View File
@@ -0,0 +1,26 @@
$ javac src/NullCase.java && java NullCase (25.0.4.1+1-LTS)
without case null: NullPointerException
with case null: null!
case null, default: null or something else
instanceof with null: false
$ javac src/NullCase.java && java NullCase (27+35)
without case null: NullPointerException
with case null: null!
case null, default: null or something else
instanceof with null: false
$ javac src/Unnamed.java && java Unnamed (21.0.10+7-Ubuntu-124.04)
src/Unnamed.java:9: error: unnamed variables are a preview feature and are disabled by default.
case Circle _ -> "round"; // the type matters, the value does not
^
(use --enable-preview to enable unnamed variables)
1 error
$ javac src/Unnamed.java && java Unnamed (25.0.4.1+1-LTS)
round angular
5
$ javac src/Unnamed.java && java Unnamed (27+35)
round angular
5
+87
View File
@@ -0,0 +1,87 @@
$ javac preview/PrimitiveInstanceof.java (25.0.4.1+1-LTS)
preview/PrimitiveInstanceof.java:3: error: primitive patterns are a preview feature and are disabled by default.
if (i instanceof byte b) return i + " fits in a byte: " + b;
^
(use --enable-preview to enable primitive patterns)
1 error
$ javac preview/PrimitiveInstanceof.java (27+35)
preview/PrimitiveInstanceof.java:3: error: primitive patterns are a preview feature and are disabled by default.
if (i instanceof byte b) return i + " fits in a byte: " + b;
^
(use --enable-preview to enable primitive patterns)
1 error
$ javac --enable-preview --release 25 preview/PrimitiveInstanceof.java && java --enable-preview PrimitiveInstanceof (25.0.4.1+1-LTS)
100 fits in a byte: 100
1000 fits in a short: 1000
100000 needs an int
0.5 converts to float 0.5 without loss
0.1 does NOT convert to float without loss
$ javac --enable-preview --release 27 preview/PrimitiveInstanceof.java && java --enable-preview PrimitiveInstanceof (27+35)
100 fits in a byte: 100
1000 fits in a short: 1000
100000 needs an int
0.5 converts to float 0.5 without loss
0.1 does NOT convert to float without loss
$ javac --enable-preview --release 25 preview/PrimitiveSwitch.java && java --enable-preview PrimitiveSwitch (25.0.4.1+1-LTS)
OK | other success 204 | client error 404 | something else 500
yes no
zero one many (9223372036854775807)
$ javac --enable-preview --release 27 preview/PrimitiveSwitch.java && java --enable-preview PrimitiveSwitch (27+35)
OK | other success 204 | client error 404 | something else 500
yes no
zero one many (9223372036854775807)
$ javac --enable-preview --release 25 preview/PrimitiveInRecord.java && java --enable-preview PrimitiveInRecord (25.0.4.1+1-LTS)
whole degrees: 21
fractional: 21.5
not a reading
$ javac --enable-preview --release 27 preview/PrimitiveInRecord.java && java --enable-preview PrimitiveInRecord (27+35)
whole degrees: 21
fractional: 21.5
not a reading
$ javac --enable-preview --release 25 preview/PrimitiveEdges.java && java --enable-preview PrimitiveEdges (25.0.4.1+1-LTS)
0.0 int? true float? true
-0.0 int? false float? true
NaN int? false float? true
1.6777217E7 int? true float? false
1.0E10 int? false float? true
3.0 int? true float? true
1099511627776 instanceof int? false
Object holding Integer matches int pattern: 42
Short in Object does not match int
65 as char: A
$ javac --enable-preview --release 27 preview/PrimitiveEdges.java && java --enable-preview PrimitiveEdges (27+35)
0.0 int? true float? true
-0.0 int? false float? true
NaN int? false float? true
1.6777217E7 int? true float? false
1.0E10 int? false float? true
3.0 int? true float? true
1099511627776 instanceof int? false
Object holding Integer matches int pattern: 42
Short in Object does not match int
65 as char: A
$ javac --enable-preview --release 25 preview/BoxedToNarrower.java && java --enable-preview BoxedToNarrower (25.0.4.1+1-LTS)
preview/BoxedToNarrower.java:4: error: incompatible types: Integer cannot be converted to byte
if (boxed instanceof byte b) System.out.println("fits a byte: " + b); // Integer to byte is not allowed
^
1 error
Error: Could not find or load main class BoxedToNarrower
Caused by: java.lang.ClassNotFoundException: BoxedToNarrower
$ javac --enable-preview --release 27 preview/BoxedToNarrower.java && java --enable-preview BoxedToNarrower (27+35)
preview/BoxedToNarrower.java:4: error: incompatible types: Integer cannot be converted to byte
if (boxed instanceof byte b) System.out.println("fits a byte: " + b); // Integer to byte is not allowed
^
1 error
Error: Could not find or load main class BoxedToNarrower
Caused by: java.lang.ClassNotFoundException: BoxedToNarrower
@@ -0,0 +1,11 @@
$ javap -v PrimitiveSwitch | grep -E 'minor|major' (compiled by 25.0.4.1+1-LTS with --enable-preview)
minor version: 65535
major version: 69
$ java --enable-preview PrimitiveSwitch (class compiled on 25, run on 27+35)
Error: LinkageError occurred while loading main class PrimitiveSwitch
java.lang.UnsupportedClassVersionError: PrimitiveSwitch (class file version 69.65535) was compiled with preview features that are unsupported. This version of the Java Runtime only recognizes preview features for class file version 71.65535
$ java PrimitiveSwitch (same class, no flag, run on 25.0.4.1+1-LTS)
Error: LinkageError occurred while loading main class PrimitiveSwitch
java.lang.UnsupportedClassVersionError: Preview features are not enabled for PrimitiveSwitch (class file version 69.65535). Try running with '--enable-preview'