原始类型模式匹配(JDK 24 预览)
大翔(CTO):「JDK 24 的模式匹配又进化了,现在可以直接匹配原始类型了!」
白歌(架构师):「没错,以前
instanceof Integer i需要自动装箱,现在instanceof int i直接匹配原始类型,性能更好。」小崔(后端开发):「那在 switch 表达式里也能用吗?」
孔蓝(后端新人):「太好了!处理 JSON 解析后的数值类型终于不用写一堆
instanceof了……」
什么是原始类型模式匹配?
| 概念 | 说明 |
|---|---|
| 传统模式匹配 | instanceof 后只能跟引用类型(如 Integer、String),匹配原始类型时需要自动装箱 |
| 原始类型模式匹配 | JDK 24 预览特性,instanceof 和 switch 中可直接使用原始类型(int、long、double 等),无需装箱 |
| 优势 | 避免不必要的装箱/拆箱,提升性能;代码更简洁直观 |
传统方式 vs JDK 24 新方式
基础用法:instanceof 中的原始类型模式
示例 1:基本使用
// === 场景说明:演示传统方式 vs JDK 24 原始类型模式匹配 ===
public class PrimitivePatternDemo {
public static void main(String[] args) {
Object obj = 42;
// 传统方式:需要自动装箱
if (obj instanceof Integer i) {
System.out.println("传统方式 - Integer 值: " + i);
}
// JDK 24 新方式:直接匹配原始类型
if (obj instanceof int i) {
System.out.println("JDK 24 方式 - int 值: " + i);
}
}
}
输出:
传统方式 - Integer 值: 42
JDK 24 方式 - int 值: 42
在 switch 中使用原始类型模式
示例 2:switch 表达式中的原始类型匹配
// === 场景说明:在 switch 表达式中匹配不同的数值类型 ===
public class SwitchPrimitiveDemo {
public static void main(String[] args) {
Object[] values = {42, 100L, 3.14, "hello", true};
for (Object obj : values) {
String result = switch (obj) {
case int i -> "int 类型,值: " + i;
case long l -> "long 类型,值: " + l;
case double d -> "double 类型,值: " + d;
case String s -> "String 类型: " + s;
default -> "其他类型: " + obj.getClass().getSimpleName();
};
System.out.println(result);
}
}
}
输出:
int 类型,值: 42
long 类型,值: 100
double 类型,值: 3.14
String 类型: hello
其他类型: Boolean
原始类型模式与引用类型模式的互斥性
重要:在同一个
switch或if-else中,int和Integer不能同时出现,因为它们匹配的是同一个值。
示例 3:互斥性演示(编译错误)
// === 场景说明:演示 int 和 Integer 不能同时使用 ===
public class MutualExclusiveDemo {
public static void main(String[] args) {
Object obj = 42;
// ❌ 编译错误:int 和 Integer 互斥
// 因为 int 是 Integer 的拆箱类型,两者不能同时匹配
/*
String result = switch (obj) {
case int i -> "int: " + i;
case Integer i -> "Integer: " + i; // 编译错误!
default -> "其他";
};
*/
}
}
正确写法:
// ✅ 正确:只使用原始类型模式
public class CorrectDemo {
public static void main(String[] args) {
Object obj = 42;
String result = switch (obj) {
case int i -> "int 值: " + i;
case long l -> "long 值: " + l;
case double d -> "double 值: " + d;
default -> "非数值类型";
};
System.out.println(result);
}
}
输出:
int 值: 42
结合守卫条件(Guard Conditions)
示例 4:使用 when 子句进行条件过滤
// === 场景说明:结合守卫条件,对匹配到的值进行进一步过滤 ===
public class GuardConditionDemo {
public static void main(String[] args) {
Object[] values = {-5, 0, 10, 20, 30};
for (Object obj : values) {
if (obj instanceof int i) {
String category = switch (i) {
case int n when n < 0 -> "负数";
case int n when n == 0 -> "零";
case int n when n > 0 && n <= 10 -> "小正数";
case int n when n > 10 -> "大正数";
default -> "未知";
};
System.out.println(i + " 是 " + category);
}
}
}
}
输出:
-5 是 负数
0 是 零
10 是 小正数
20 是 大正数
30 是 大正数
实战场景
场景 1:飞翔科技监控指标路由
// === 场景说明:监控系统中,不同数值类型需要不同的处理逻辑 ===
public class MonitoringDemo {
// 模拟监控指标数据
record Metric(String name, Object value) {}
public static void main(String[] args) {
Metric[] metrics = {
new Metric("cpu_usage", 85),
new Metric("memory_total", 16L * 1024 * 1024 * 1024), // 16GB in bytes
new Metric("load_average", 1.25),
new Metric("hostname", "server-01")
};
for (Metric metric : metrics) {
processMetric(metric);
}
}
static void processMetric(Metric metric) {
String result = switch (metric.value()) {
case int i when i >= 0 && i <= 100 ->
"指标 [%s]: CPU/百分比类型,值=%d".formatted(metric.name(), i);
case long l ->
"指标 [%s]: 内存/磁盘类型,值=%d bytes".formatted(metric.name(), l);
case double d ->
"指标 [%s]: 负载/比率类型,值=%.2f".formatted(metric.name(), d);
case String s ->
"指标 [%s]: 标签类型,值=%s".formatted(metric.name(), s);
default ->
"指标 [%s]: 未知类型".formatted(metric.name());
};
System.out.println(result);
}
}
输出:
指标 [cpu_usage]: CPU/百分比类型,值=85
指标 [memory_total]: 内存/磁盘类型,值=17179869184 bytes
指标 [load_average]: 负载/比率类型,值=1.25
指标 [hostname]: 标签类型,值=server-01
场景 2:JSON 解析后的数值类型处理
// === 场景说明:模拟 JSON 解析后,数值可能是不同数值类型 ===
public class JsonParseDemo {
public static void main(String[] args) {
// 模拟 JSON 解析后的数值(实际项目中可能是 Jackson/Gson 解析结果)
Object[] jsonValues = {100, 100L, 100.0, "100"};
for (Object val : jsonValues) {
handleJsonValue(val);
}
}
static void handleJsonValue(Object val) {
String typeInfo = switch (val) {
case int i -> "JSON 数值(int): " + i + ",适合计数/枚举";
case long l -> "JSON 数值(long): " + l + ",适合大数据ID/时间戳";
case double d -> "JSON 数值(double): " + d + ",适合金额/比率";
case String s -> "JSON 字符串: " + s;
default -> "未知类型";
};
System.out.println(typeInfo);
}
}
输出:
JSON 数值(int): 100,适合计数/枚举
JSON 数值(long): 100,适合大数据ID/时间戳
JSON 数值(double): 100.0,适合金额/比率
JSON 字符串: 100
编译与运行
注意:原始类型模式匹配是 JDK 24 的预览特性,编译和运行时需要启用预览功能。
# 编译时启用预览特性
javac --enable-preview --release 24 PrimitivePatternDemo.java
# 运行时启用预览特性
java --enable-preview PrimitivePatternDemo
易错场景
易错点 1:int 和 Integer 不能同时使用
// ❌ 错误:在同一个 switch 中同时使用 int 和 Integer
Object obj = 42;
String result = switch (obj) {
case int i -> "int: " + i;
case Integer i -> "Integer: " + i; // 编译错误!互斥
default -> "其他";
};
// ✅ 正确:只使用原始类型
Object obj = 42;
String result = switch (obj) {
case int i -> "int: " + i;
case long l -> "long: " + l;
default -> "其他";
};
易错点 2:忘记启用预览特性
// ❌ 错误:未启用预览特性直接编译
// javac PrimitivePatternDemo.java
// 错误:原始类型模式匹配是预览特性,需要 --enable-preview
# ✅ 正确:启用预览特性
javac --enable-preview --release 24 PrimitivePatternDemo.java
java --enable-preview PrimitivePatternDemo
易错点 3:float 和 boolean 原始类型暂不支持
// ❌ 错误:目前只支持 int、long、double、char
Object obj = 3.14f;
// case float f -> ... // 目前不支持!
// ✅ 正确:使用 double 代替 float
Object obj = 3.14;
if (obj instanceof double d) {
System.out.println("double 值: " + d);
}
面试考点
问题 1:JDK 24 的原始类型模式匹配解决了什么问题?
答案: 传统模式匹配中,
instanceof只能匹配引用类型,当需要用原始类型时,会发生自动装箱(如int→Integer),这会引入不必要的对象创建和性能开销。原始类型模式匹配允许instanceof和switch直接匹配int、long、double等原始类型,避免了装箱/拆箱的性能损耗,同时让代码更简洁直观。
问题 2:int 和 Integer 在模式匹配中为什么不能同时使用?
答案: 因为
int是Integer的拆箱类型,在模式匹配中,它们匹配的是同一个值。如果同时写case int i和case Integer i,会导致编译错误,因为这两个分支是互斥的(同一个值不可能同时匹配两个互斥的类型模式)。这是 Java 编译器为了保证类型安全而做的限制。
问题 3:原始类型模式匹配目前支持哪些原始类型?
答案: 目前 JDK 24 的原始类型模式匹配支持以下原始类型:
intlongdoublechar暂不支持
float、byte、short、boolean。如果需要匹配float,可以使用double代替;如果需要匹配boolean,可以使用引用类型Boolean。
总结
| 特性 | 传统方式 | JDK 24 新方式 |
|---|---|---|
instanceof 匹配 | obj instanceof Integer i | obj instanceof int i |
switch 匹配 | case Integer i | case int i |
| 性能 | 需要自动装箱 | 无装箱,性能更好 |
| 代码简洁度 | 较繁琐 | 更简洁 |
| 预览特性 | 否 | 是,需要 --enable-preview |
「代码是写给人看的,顺便让机器运行。」—— Harold Abelson