JDK16 Pattern Matching for instanceof:告别冗余的类型强转
2026/4/30大约 11 分钟
JDK16 Pattern Matching for instanceof:告别冗余的类型强转
适读人群:写过大量 instanceof + 强转代码、对Java模式匹配感兴趣的开发者 | 阅读时长:约13分钟
开篇故事
我入行那年,写了很多这样的代码:
if (shape instanceof Circle) {
Circle c = (Circle) shape; // 刚判断完类型,又强转一次
System.out.println(c.getRadius());
}这段代码有一个让我一直觉得奇怪的地方:instanceof已经告诉你这是个Circle了,为什么还要再手动(Circle) shape强转一次?这不是重复信息吗?
我问过一个老前辈,他说:"Java就这样,忍着吧。"
忍了将近20年,JDK16终于不用再忍了。
if (shape instanceof Circle c) { // 一次搞定
System.out.println(c.getRadius());
}别小看这个改动,它背后代表着Java走向模式匹配的第一步,和后续的Switch Pattern Matching、Record Patterns形成了完整的模式匹配体系。
一、Pattern Matching的背景
1.1 原来的instanceof有多烦
统计了一下我们项目里的老代码,典型的instanceof使用模式:
// 场景1:类型分发处理(最常见)
void processShape(Shape shape) {
if (shape instanceof Circle) {
Circle c = (Circle) shape; // 冗余!
double area = Math.PI * c.getRadius() * c.getRadius();
System.out.println("Circle area: " + area);
} else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape; // 又一次冗余!
double area = r.getWidth() * r.getHeight();
System.out.println("Rectangle area: " + area);
} else if (shape instanceof Triangle) {
Triangle t = (Triangle) shape; // 还是冗余!
System.out.println("Triangle: " + t);
}
}
// 场景2:equals方法
@Override
public boolean equals(Object o) {
if (!(o instanceof MyClass)) return false;
MyClass other = (MyClass) o; // 又强转了
return Objects.equals(this.id, other.id);
}每一个instanceof后面都跟着一个强转,编译器明明知道,却要你再写一遍。
1.2 引入版本时间线
JDK14:Pattern Matching for instanceof(Preview,JEP 305)
JDK15:第二次Preview
JDK16:正式GA(2021年3月,JEP 394)
后续延伸:
JDK17:Switch Pattern Matching(Preview)
JDK21:Switch Pattern Matching GA,Record Patterns GA二、Pattern Matching for instanceof深度解析
2.1 基本语法
// 语法格式:
// if (obj instanceof TypeName variableName)
// 如果obj是TypeName的实例,则:
// 1. 条件为true
// 2. 变量variableName在true分支中可用,类型为TypeName
// 3. variableName是一个"模式变量"(Pattern Variable)
// 基本用法
Object obj = "hello";
if (obj instanceof String s) {
System.out.println(s.length()); // s在这里类型是String
}
// s在这里不可用(作用域仅在if内部)
// 配合额外条件
if (obj instanceof String s && s.length() > 3) {
System.out.println("Long string: " + s.toUpperCase());
}
// 注意:可以在同一个&&条件里使用s,因为&&短路保证了s的有效性
// 但||不能这样用:if (obj instanceof String s || s.startsWith("x"))
// 因为||在obj不是String时,s未绑定就会被访问2.2 模式变量的作用域规则
作用域规则("Definite Assignment"规则):
if (obj instanceof String s) {
// s可用 ← true分支
} else {
// s不可用 ← false分支
}
// 模式变量s在外部不可用(if结束后)
// 特殊情况:否定+提前返回
if (!(obj instanceof String s)) {
return; // 提前返回
}
// 这里s可用!因为执行到这里意味着obj确实是String
System.out.println(s.length()); // OKMermaid图:
2.3 对equals方法的改进
这是最常用的一个场景:
// 旧写法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point)) return false;
Point point = (Point) o; // 冗余强转
return x == point.x && y == point.y;
}
// 新写法(JDK16+)
@Override
public boolean equals(Object o) {
return o instanceof Point p && x == p.x && y == p.y;
// 一行搞定:instanceof检查 + 模式变量 + 字段比较
}三、完整代码示例
3.1 类型分发:旧写法到新写法
import java.util.*;
/**
* Pattern Matching for instanceof完整示例
* 引入版本:JDK14 Preview;GA版本:JDK16(2021年3月,JEP 394)
*/
public class PatternMatchingDemo {
// 形状体系
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
// ===== 旧写法:instanceof + 强转 =====
static double calculateAreaOld(Shape shape) {
if (shape instanceof Circle) {
Circle c = (Circle) shape; // 重复判断
return Math.PI * c.radius() * c.radius();
} else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape; // 重复判断
return r.width() * r.height();
} else if (shape instanceof Triangle) {
Triangle t = (Triangle) shape; // 重复判断
return 0.5 * t.base() * t.height();
}
throw new IllegalArgumentException("Unknown shape: " + shape);
}
// ===== 新写法:Pattern Matching =====
static double calculateAreaNew(Shape shape) {
if (shape instanceof Circle c) {
return Math.PI * c.radius() * c.radius();
} else if (shape instanceof Rectangle r) {
return r.width() * r.height();
} else if (shape instanceof Triangle t) {
return 0.5 * t.base() * t.height();
}
throw new IllegalArgumentException("Unknown shape: " + shape);
}
// ===== 更新的写法(JDK17+ Switch Pattern Matching)=====
static double calculateAreaSwitch(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
};
}
// ===== equals方法改进 =====
record Point(int x, int y) {
// 旧写法:
// @Override
// public boolean equals(Object o) {
// if (this == o) return true;
// if (!(o instanceof Point)) return false;
// Point point = (Point) o;
// return x == point.x && y == point.y;
// }
// 新写法:(Record自动生成更好,这里只是演示)
@Override
public boolean equals(Object o) {
return o instanceof Point p && x == p.x && y == p.y;
}
}
// ===== 复杂条件组合 =====
static void processObject(Object obj) {
// 带条件的模式匹配
if (obj instanceof String s && s.startsWith("http")) {
System.out.println("URL: " + s.toUpperCase());
} else if (obj instanceof String s && s.length() > 10) {
System.out.println("Long string: " + s.substring(0, 10) + "...");
} else if (obj instanceof String s) {
System.out.println("Short string: " + s);
} else if (obj instanceof Integer i && i > 0) {
System.out.println("Positive int: " + i);
} else if (obj instanceof Integer i) {
System.out.println("Non-positive int: " + i);
} else if (obj instanceof List<?> list && !list.isEmpty()) {
System.out.println("Non-empty list: " + list.get(0));
} else if (obj == null) {
System.out.println("null value");
} else {
System.out.println("Other type: " + obj.getClass().getName());
}
}
// ===== 提前return模式(消除嵌套)=====
static String extractUsername(Object token) {
// 旧写法:深层嵌套
// if (token instanceof Map) {
// Map<?, ?> map = (Map<?, ?>) token;
// Object data = map.get("data");
// if (data instanceof Map) {
// Map<?, ?> dataMap = (Map<?, ?>) data;
// Object user = dataMap.get("user");
// if (user instanceof String) {
// return (String) user;
// }
// }
// }
// return null;
// 新写法:提前return + 模式匹配
if (!(token instanceof Map<?, ?> tokenMap)) return null;
var data = tokenMap.get("data");
if (!(data instanceof Map<?, ?> dataMap)) return null;
var user = dataMap.get("user");
if (!(user instanceof String username)) return null;
return username; // username在这里可用
}
// ===== 泛型类型的模式匹配 =====
static <T> T getFirst(Object container) {
if (container instanceof List<?> list && !list.isEmpty()) {
@SuppressWarnings("unchecked")
T first = (T) list.get(0);
return first;
} else if (container instanceof Optional<?> opt && opt.isPresent()) {
@SuppressWarnings("unchecked")
T value = (T) opt.get();
return value;
}
return null;
}
public static void main(String[] args) {
List<Shape> shapes = List.of(
new Circle(5.0),
new Rectangle(3.0, 4.0),
new Triangle(6.0, 4.0)
);
for (var shape : shapes) {
System.out.printf("%s: area = %.2f%n", shape, calculateAreaNew(shape));
}
System.out.println("\n=== processObject ===");
processObject("https://example.com");
processObject("hello");
processObject(42);
processObject(-10);
processObject(List.of("first", "second"));
System.out.println("\n=== extractUsername ===");
var token = Map.of("data", Map.of("user", "alice"));
System.out.println(extractUsername(token));
System.out.println(extractUsername(null));
System.out.println(extractUsername("invalid"));
}
}3.2 结合Record和Sealed Classes的完整实战
import java.util.*;
/**
* Pattern Matching + Record + Sealed Classes 三件套
* 实现一个简单的表达式求值器
*/
public class ExpressionEvaluator {
// Sealed接口定义表达式类型
sealed interface Expr
permits Num, Add, Mul, Neg, Var {}
record Num(double value) implements Expr {}
record Add(Expr left, Expr right) implements Expr {}
record Mul(Expr left, Expr right) implements Expr {}
record Neg(Expr expr) implements Expr {}
record Var(String name) implements Expr {}
// ===== 旧写法(JDK16之前)=====
static double evaluateOld(Expr expr, Map<String, Double> env) {
if (expr instanceof Num) {
return ((Num) expr).value();
} else if (expr instanceof Add) {
Add add = (Add) expr;
return evaluateOld(add.left(), env) + evaluateOld(add.right(), env);
} else if (expr instanceof Mul) {
Mul mul = (Mul) expr;
return evaluateOld(mul.left(), env) * evaluateOld(mul.right(), env);
} else if (expr instanceof Neg) {
return -evaluateOld(((Neg) expr).expr(), env);
} else if (expr instanceof Var) {
String name = ((Var) expr).name();
return env.getOrDefault(name, 0.0);
}
throw new IllegalArgumentException("Unknown expr: " + expr);
}
// ===== 新写法(JDK16+ Pattern Matching)=====
static double evaluate(Expr expr, Map<String, Double> env) {
if (expr instanceof Num n) {
return n.value();
} else if (expr instanceof Add a) {
return evaluate(a.left(), env) + evaluate(a.right(), env);
} else if (expr instanceof Mul m) {
return evaluate(m.left(), env) * evaluate(m.right(), env);
} else if (expr instanceof Neg neg) {
return -evaluate(neg.expr(), env);
} else if (expr instanceof Var v) {
return env.getOrDefault(v.name(), 0.0);
}
throw new IllegalArgumentException("Unknown expr: " + expr);
}
// ===== 最新写法(JDK21 Switch Pattern Matching)=====
static double evaluateSwitch(Expr expr, Map<String, Double> env) {
return switch (expr) {
case Num n -> n.value();
case Add a -> evaluateSwitch(a.left(), env) + evaluateSwitch(a.right(), env);
case Mul m -> evaluateSwitch(m.left(), env) * evaluateSwitch(m.right(), env);
case Neg neg -> -evaluateSwitch(neg.expr(), env);
case Var v -> env.getOrDefault(v.name(), 0.0);
};
}
// 打印表达式
static String prettyPrint(Expr expr) {
return switch (expr) {
case Num n -> String.valueOf(n.value());
case Add a -> "(" + prettyPrint(a.left()) + " + " + prettyPrint(a.right()) + ")";
case Mul m -> "(" + prettyPrint(m.left()) + " * " + prettyPrint(m.right()) + ")";
case Neg neg -> "-" + prettyPrint(neg.expr());
case Var v -> v.name();
};
}
public static void main(String[] args) {
// 构建表达式: (x + 2) * (y - 3)
// = (x + 2) * (y + (-3))
Expr e = new Mul(
new Add(new Var("x"), new Num(2)),
new Add(new Var("y"), new Neg(new Num(3)))
);
Map<String, Double> env = Map.of("x", 5.0, "y", 10.0);
System.out.println("Expression: " + prettyPrint(e));
System.out.println("Value: " + evaluate(e, env)); // (5+2) * (10-3) = 49.0
System.out.println("Value (switch): " + evaluateSwitch(e, env));
}
}3.3 实际项目中的应用:JSON解析结果处理
import java.util.*;
/**
* 实际项目场景:处理JSON解析后的对象树(Object类型的Map/List混合)
*/
public class JsonTreeProcessor {
// 模拟JSON解析结果(Jackson/Gson解析后的原始类型)
// ===== 旧写法:大量instanceof+强转 =====
static void printOld(String indent, Object value) {
if (value == null) {
System.out.println(indent + "null");
} else if (value instanceof Map) {
Map<?, ?> map = (Map<?, ?>) value;
for (Map.Entry<?, ?> entry : map.entrySet()) {
System.out.println(indent + entry.getKey() + ":");
printOld(indent + " ", entry.getValue());
}
} else if (value instanceof List) {
List<?> list = (List<?>) value;
for (int i = 0; i < list.size(); i++) {
System.out.println(indent + "[" + i + "]:");
printOld(indent + " ", list.get(i));
}
} else if (value instanceof String) {
System.out.println(indent + "\"" + value + "\"");
} else if (value instanceof Number) {
System.out.println(indent + value);
} else if (value instanceof Boolean) {
System.out.println(indent + value);
} else {
System.out.println(indent + value.toString());
}
}
// ===== 新写法:Pattern Matching =====
static void print(String indent, Object value) {
if (value == null) {
System.out.println(indent + "null");
} else if (value instanceof Map<?, ?> map) {
map.forEach((k, v) -> {
System.out.println(indent + k + ":");
print(indent + " ", v);
});
} else if (value instanceof List<?> list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(indent + "[" + i + "]:");
print(indent + " ", list.get(i));
}
} else if (value instanceof String s) {
System.out.println(indent + "\"" + s + "\"");
} else if (value instanceof Number n) {
System.out.println(indent + n);
} else if (value instanceof Boolean b) {
System.out.println(indent + b);
} else {
System.out.println(indent + value);
}
}
// 提取嵌套路径的值(模拟JSONPath)
@SuppressWarnings("unchecked")
static Optional<Object> extract(Object root, String... path) {
Object current = root;
for (String key : path) {
if (current instanceof Map<?, ?> map) {
current = map.get(key);
} else if (current instanceof List<?> list) {
try {
int index = Integer.parseInt(key);
if (index >= 0 && index < list.size()) {
current = list.get(index);
} else {
return Optional.empty();
}
} catch (NumberFormatException e) {
return Optional.empty();
}
} else {
return Optional.empty();
}
if (current == null) return Optional.empty();
}
return Optional.ofNullable(current);
}
public static void main(String[] args) {
// 模拟JSON对象
Map<String, Object> json = new LinkedHashMap<>();
json.put("name", "Alice");
json.put("age", 30);
json.put("active", true);
json.put("scores", List.of(95, 87, 92));
json.put("address", Map.of(
"city", "Beijing",
"zip", "100000"
));
System.out.println("=== JSON Tree ===");
print("", json);
System.out.println("\n=== Extract ===");
System.out.println(extract(json, "name")); // Optional[Alice]
System.out.println(extract(json, "address", "city")); // Optional[Beijing]
System.out.println(extract(json, "scores", "1")); // Optional[87]
System.out.println(extract(json, "missing")); // Optional.empty
}
}四、踩坑实录
坑1:模式变量的作用域边界
public class PatternScopeTrap {
public static void main(String[] args) {
Object obj = "hello";
// 正确:&&条件中可以使用模式变量
if (obj instanceof String s && s.length() > 3) {
System.out.println(s); // OK
}
// 错误:||条件中不能使用(||左边失败时,右边s未绑定)
// if (obj instanceof String s || s.startsWith("x")) { } // 编译错误
// 陷阱:下面代码能编译,但逻辑可能不对
Object result = null;
if (obj instanceof String s) {
result = s.toUpperCase();
}
// 注意:s在这里已经不可用了,不要尝试在if外面使用
// System.out.println(s); // 编译错误
// 正确的提前return模式
if (!(obj instanceof String s)) {
throw new IllegalArgumentException("Not a string");
}
// 这里s可用,因为执行到这里说明obj必定是String
System.out.println(s.length()); // OK
}
}坑2:模式变量名遮蔽外部变量
public class PatternShadowTrap {
static String s = "global";
public static void main(String[] args) {
Object obj = "local";
if (obj instanceof String s) {
// 这里的s是模式变量,遮蔽了static字段s
System.out.println(s); // "local"(模式变量)
System.out.println(PatternShadowTrap.s); // "global"(显式访问静态字段)
}
// 避免命名冲突:选择有意义的变量名
if (obj instanceof String str) { // 用str而不是s
System.out.println(str);
}
}
}坑3:null不匹配任何Pattern
public class PatternNullTrap {
public static void main(String[] args) {
Object obj = null;
// instanceof对null总是false
System.out.println(obj instanceof String s); // false(不是true,也不抛NPE)
// 所以下面的代码中,null不会进入任何分支
if (obj instanceof String s) {
System.out.println("String: " + s); // 不执行
} else if (obj instanceof Integer i) {
System.out.println("Integer: " + i); // 不执行
} else {
System.out.println("Not matched (or null)"); // 执行这里
}
// 如果需要处理null,必须显式检查
if (obj == null) {
System.out.println("null");
} else if (obj instanceof String s) {
System.out.println("String: " + s);
}
}
}坑4:配合泛型使用的局限
import java.util.*;
public class PatternWithGenericsTrap {
public static void main(String[] args) {
Object obj = List.of("hello", "world");
// 可以匹配原始类型List<?>
if (obj instanceof List<?> list) {
System.out.println("List size: " + list.size()); // OK
}
// 不能匹配具体泛型类型(类型擦除)
// if (obj instanceof List<String> list) { // 编译警告:unchecked
// list.get(0); // 可能ClassCastException
// }
// 正确做法:先匹配原始类型,再检查元素类型
if (obj instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof String) {
@SuppressWarnings("unchecked")
List<String> strings = (List<String>) list;
System.out.println("First: " + strings.get(0));
}
}
}坑5:过度使用instanceof导致OCP违反
// 设计反模式:过多的instanceof判断通常意味着设计需要改进
// 应该考虑使用多态替代
// 反模式:
void processShape(Shape shape) {
if (shape instanceof Circle c) { ... }
else if (shape instanceof Rectangle r) { ... }
else if (shape instanceof Triangle t) { ... }
// 每加一种Shape类型,就要改这个方法(违反开闭原则)
}
// 更好的设计(使用多态):
interface Shape {
double area(); // 每个Shape自己实现area()
String describe();
}
// 但如果用Sealed Classes + Switch Pattern Matching:
sealed interface Shape permits Circle, Rectangle, Triangle {
// 不需要在接口里定义area()
}
// 然后用switch pattern matching集中处理:
// 这样加新Shape时,编译器会提醒你在switch里添加分支
// 在某些设计中(如访问者模式)这是合理的五、总结与延伸
5.1 Pattern Matching的演进路线
JDK16:Pattern Matching for instanceof(基础)
↓
JDK17 Preview / JDK21 GA:Pattern Matching for switch
↓
JDK21 GA:Record Patterns(解构Pattern)
↓
未来:更多Pattern类型(数组Pattern等)5.2 版本兼容建议
| 版本 | 特性 | 建议 |
|---|---|---|
| JDK16 | instanceof Pattern Matching GA | 可用,推荐在equals()等地方使用 |
| JDK17(LTS) | Sealed Classes GA | 配合instanceof使用,效果更好 |
| JDK21(LTS) | Switch Pattern + Record Patterns GA | 全面拥抱模式匹配体系 |
5.3 对代码质量的影响
Pattern Matching最大的价值不是减少代码量,而是:
- 提高代码清晰度:意图更明确,减少噪音代码
- 减少潜在Bug:消除了忘记强转导致的ClassCastException
- 编译期安全:结合Sealed Classes,编译器可以检查模式的完整性
