instanceof 模式匹配#
版本信息
- 最低 JDK:16(14/15 预览 → 16 正式,17 LTS GA)
- JEP:JEP 394: Pattern Matching for instanceof
为什么需要#
JDK 8 里判断类型再使用,标准三件套是「instanceof 判断 + 显式强转 + 用强转后的变量」:
if (obj instanceof String) {
String s = (String) obj; // 又写一遍类型名,纯样板
return s.length();
}
类型名写了两次、强转可有可无地写错、equals 判类型还要先 getClass()。JEP 394 让 instanceof 匹配成功即绑定一个模式变量——变量自动是目标类型,省去强转,作用域也精确到「确实匹配」的分支里。
语法#
examples/instanceof-pattern/ 里真实可运行的源码(snippets 嵌入,与示例零漂移):
package com.javamodern.pminstance;
// JEP 394(JDK 16):instanceof 匹配即「绑定模式变量」,
// 消除 JDK 8 里「先 instanceof 判断、再强转使用」的样板。
public class PatternInstanceofDemo {
public static String describe(Object obj) {
if (obj instanceof String s) { // (1) 匹配则绑定 s,s 已是 String,无需强转
return "字符串:" + s;
}
if (obj instanceof Integer i) {
return "数字:" + i;
}
return "未知"; // null 或其它类型(null instanceof T 恒为 false)
}
public static void main(String[] args) {
System.out.println(describe("hi")); // 字符串:hi
System.out.println(describe(42)); // 数字:42
System.out.println(describe(null)); // 未知
}
}
- :material-lightbulb:
obj instanceof String s——匹配则s被绑定且已是String,无需强转;分支内直接用s。
运行 PatternInstanceofDemo.main 的实测输出(JDK 17):
字符串:hi
数字:42
未知
与 JDK 8 旧写法对比#
if (obj instanceof String) {
String s = (String) obj; // 显式强转,类型名写两遍
return "字符串:" + s;
} else if (obj instanceof Integer) {
Integer i = (Integer) obj;
return "数字:" + i;
}
return "未知";
if (obj instanceof String s) { // 匹配即绑定,s 已是 String
return "字符串:" + s;
} else if (obj instanceof Integer i) {
return "数字:" + i;
}
return "未知";
底层原理#
instanceof T x 是类型模式(type pattern):运行时若对象确实是 T,就把对象绑定到变量 x(其静态类型即 T),分支内编译器已知 x: T,故无需强转。关键是作用域的精确性(flow typing):x 只在「编译器能证明 instanceof 成立」的范围内可用——if 的 then 分支、&& 的右侧、! 取反后的 else 分支等;null instanceof T x 恒为 false,所以 x 不会绑定到 null。
graph LR
C["obj instanceof String s"] --> M{"运行时 obj 是 String?"}
M -- 是 --> B["绑定 s(类型 String)<br/>分支内直接用,无需强转"]
M -- 否 --> N["s 不在作用域"]
M -.obj 为 null.- N
style C fill:#bbdefb
style B fill:#c8e6c9
常见坑 / 最佳实践#
null永不匹配:null instanceof String s为false,s不会被绑定;这点和旧instanceof一致。- 作用域随控制流收缩:
if (!(obj instanceof String s)) return;之后,s也可用(取反后往下即「确实匹配」)。但跨方法、跨赋值后作用域可能消失,编译器会提示。 - 与
switch模式匹配(JDK 21)配套:分支多了用instanceof串联会变长,JDK 21 的 switch 模式匹配 是它的自然演进。 - 不要为了用而拆:如果分支只有一处类型判断,
instanceof模式变量已足够;多分支再上switch模式匹配。
小结#
instanceof 模式匹配消除「判断 + 强转」样板,让类型分支更安全、更短。它是 JDK 17 语言特性里最日常、收益最直接的一项,也是后续 record 解构、switch 模式匹配的基石。