JEP 455 introduced Primitive Types in Patterns, instanceof, and switch as a preview feature in JDK 23. It continues as a preview feature in JDK 24 and 25, so the feature is not detailed in the Java Language Specification.
The JEP describes "safety of conversions", where certain pairs of types are unconditionally exact, meaning no runtime checks are required. For other pairs, a runtime test is necessary to determine if a conversion can be made without loss of information.
For example:
int x = getValue(); // Which returns 42
switch (x) {
case byte -> System.out.println("byte");
case int -> System.out.println("int");
}
The runtime check verifies that 42 will fit in a byte and matches, printing 'byte'.
If we change the order of the cases:
switch (x) {
case int -> System.out.println("int");
case byte -> System.out.println("byte");
}
This will result in a compiler error because the int case now dominates the byte case.
Quoting from the Java Language Specification (where pattern dominance is defined),
A switch label in a switch block is said to be dominated if for every value that it applies to, it can be determined that one of the preceding switch labels would also apply.
So far, everything makes sense.
However, if we change the case for int to use the wrapper class Integer:
switch (x) {
case Integer -> System.out.println("int");
case byte -> System.out.println("byte");
}
This code will compile without an error, and the result will be 'int'.
The compiler is clearly autoboxing the int to an Integer and matching the first case.
What I can't understand is why the compiler allows this, since the Integer case is still dominating the byte case. Any value that will match for a byte will also match for a boxed Integer.
JEP 507, the latest version states:
Furthermore, boxing conversions and widening reference conversions are unconditionally exact.
However, that still does not explain why the compiler is not rejecting this because of pattern dominance.
Just to be clear, all the examples use the same line, int x = getValue();, where getValue() returns 42.