Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
96 lines
4.1 KiB
Java
96 lines
4.1 KiB
Java
/**
|
|
* JEP 532, Primitive Types in Patterns, instanceof, and switch (fifth preview).
|
|
*
|
|
* <p>Until now patterns worked on reference types only. With this preview a pattern can name a
|
|
* primitive type, and the test is "can this value be converted to that type WITHOUT losing
|
|
* information?" Explained in docs/05-primitive-patterns.md. Compile and run with --enable-preview.
|
|
*/
|
|
public class PrimitivePatterns {
|
|
|
|
record Reading(int celsius) {}
|
|
|
|
public static void main(String[] args) {
|
|
exactness();
|
|
switchOnLong();
|
|
switchOnObject();
|
|
recordPattern();
|
|
}
|
|
|
|
/** instanceof with a primitive type asks: is this an EXACT conversion? */
|
|
static void exactness() {
|
|
System.out.println("--- instanceof <primitive>: an exact-conversion test, not a cast");
|
|
int small = 100, big = 300;
|
|
boolean a = small instanceof byte;
|
|
boolean b = big instanceof byte;
|
|
System.out.println("100 instanceof byte = " + a);
|
|
System.out.println("300 instanceof byte = " + b + " (a byte holds -128..127)");
|
|
Check.that(a && !b, "100 converts to byte exactly, 300 does not");
|
|
|
|
if (small instanceof byte narrow) {
|
|
System.out.println("binding: byte narrow = " + narrow);
|
|
}
|
|
|
|
int precise = 16_777_217; // 2^24 + 1: the first int a float cannot hold
|
|
boolean f1 = 16_777_216 instanceof float;
|
|
boolean f2 = precise instanceof float;
|
|
System.out.println("16_777_216 instanceof float = " + f1);
|
|
System.out.println("16_777_217 instanceof float = " + f2 + " (float has a 24-bit significand)");
|
|
Check.that(f1 && !f2, "int to float is exact up to 2^24 and lossy after");
|
|
}
|
|
|
|
/** switch over a long, with a primitive pattern and a guard. */
|
|
static String classify(long value) {
|
|
return switch (value) {
|
|
case 0L -> "zero";
|
|
case 1L -> "one";
|
|
case long v when v < 0 -> "negative (" + v + ")";
|
|
case long v -> "positive (" + v + ")";
|
|
};
|
|
}
|
|
|
|
static void switchOnLong() {
|
|
System.out.println("--- switch on a long, with pattern labels and a guard");
|
|
for (long v : new long[] {0, 1, -7, 9_000_000_000L}) {
|
|
System.out.println("classify(" + v + ") = " + classify(v));
|
|
}
|
|
Check.that(classify(0).equals("zero") && classify(-7).startsWith("negative"), "long switch dispatches on constants, then patterns");
|
|
}
|
|
|
|
/** switch over an Object: primitive patterns match the boxed types they correspond to. */
|
|
static String describe(Object o) {
|
|
return switch (o) {
|
|
case int i when i > 1000 -> "a large int " + i;
|
|
case int i -> "an int " + i;
|
|
case long l -> "a long " + l;
|
|
case double d -> "a double " + d;
|
|
case String s -> "a String of length " + s.length();
|
|
default -> "something else: " + o;
|
|
};
|
|
}
|
|
|
|
static void switchOnObject() {
|
|
System.out.println("--- switch on an Object: an Integer matches 'case int'");
|
|
Object[] samples = {7, 5000, 7L, 2.5, "hello", 'x'};
|
|
for (Object o : samples) {
|
|
System.out.println("describe(" + o.getClass().getSimpleName() + " " + o + ") = " + describe(o));
|
|
}
|
|
Check.that(describe(5000).equals("a large int 5000"), "an Integer 5000 matches case int with a guard");
|
|
}
|
|
|
|
/** Record patterns can now narrow a component: Reading.celsius is an int, the pattern asks for a byte. */
|
|
static void recordPattern() {
|
|
System.out.println("--- record pattern with a narrowing primitive component");
|
|
Object[] readings = {new Reading(36), new Reading(4_000)};
|
|
for (Object o : readings) {
|
|
String verdict = switch (o) {
|
|
case Reading(byte c) -> "fits in a byte: " + c;
|
|
case Reading(int c) -> "needs a full int: " + c;
|
|
default -> "not a reading";
|
|
};
|
|
System.out.println(o + " -> " + verdict);
|
|
}
|
|
Check.that(readings[0] instanceof Reading(byte _) && !(readings[1] instanceof Reading(byte _)),
|
|
"Reading(byte c) matches 36 but not 4000");
|
|
}
|
|
}
|