JDK21 Record Patterns:解构赋值让模式匹配更强大
2026/4/30大约 10 分钟
JDK21 Record Patterns:解构赋值让模式匹配更强大
适读人群:使用Record的开发者、对模式匹配体系感兴趣的Java开发者 | 阅读时长:约15分钟
开篇故事
2023年,我们项目里有大量处理业务事件的代码,事件用Record定义:
record OrderEvent(String orderId, OrderStatus status, double amount) {}
record UserEvent(long userId, String action, String details) {}每次处理这些事件都要先强转,再访问字段:
if (event instanceof OrderEvent) {
OrderEvent oe = (OrderEvent) event; // 强转
String orderId = oe.orderId(); // 访问字段
double amount = oe.amount(); // 访问字段
// 处理逻辑
}JDK16的instanceof Pattern Matching已经改进了一步:
if (event instanceof OrderEvent oe) {
// 直接用oe.orderId()
}但JDK21的Record Patterns更进一步,直接在模式里解构:
if (event instanceof OrderEvent(var orderId, var status, var amount)) {
// 直接用orderId, status, amount,不用再.orderId()了
}这就是解构赋值。Kotlin、Python、JavaScript早就有了,Java在JDK21终于补上了这个能力。
一、Record Patterns的设计背景
1.1 版本时间线
JDK19:Record Patterns Preview(JEP 405)
JDK20:第二次Preview(JEP 432)
JDK21:Record Patterns GA(2023年9月,JEP 440)1.2 为什么需要解构?
传统的访问Record字段方式:record.field(),有三个问题:
- 需要先给record命名,再通过名字访问字段
- 嵌套Record访问层级深时代码冗长
- 结合switch pattern matching时不够简洁
Record Patterns解决了这些问题:直接在模式里声明变量名,JVM在匹配成功时自动赋值。
二、Record Patterns深度解析
2.1 基本语法
// 语法:TypeName(pattern1, pattern2, ...)
// 其中pattern可以是:
// - 类型模式:var x 或 TypeName x
// - 嵌套Record Pattern
// - 通配符 _ (忽略该组件,JDK21暂不支持,JDK22+支持)
record Point(int x, int y) {}
record Line(Point start, Point end) {}
// 基本解构
Object obj = new Point(3, 4);
if (obj instanceof Point(int x, int y)) {
System.out.println("x=" + x + ", y=" + y);
}
// 使用var推断类型
if (obj instanceof Point(var x, var y)) {
System.out.println("x=" + x + ", y=" + y);
}
// 嵌套解构
Object line = new Line(new Point(0, 0), new Point(3, 4));
if (line instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
System.out.println("start=(" + x1 + "," + y1 + "), end=(" + x2 + "," + y2 + ")");
}2.2 与Switch的结合(最强大的用法)
2.3 与sealed类型的天作之合
Sealed Classes + Record + Pattern Matching + Record Patterns,是JDK21最强大的组合。
三、完整代码示例
3.1 基础解构示例:旧写法vs新写法
import java.util.*;
import java.util.function.*;
/**
* Record Patterns完整示例
* 引入版本:JDK19 Preview;GA版本:JDK21(2023年9月,JEP 440)
*/
public class RecordPatternsDemo {
// 业务模型
record Point(double x, double y) {}
record Circle(Point center, double radius) {}
record Rectangle(Point topLeft, Point bottomRight) {}
sealed interface Shape permits Circle, Rectangle {}
record ShapeCircle(Point center, double radius) implements Shape {}
record ShapeRectangle(Point topLeft, Point bottomRight) implements Shape {}
// ===== 旧写法 =====
static double areaOld(Shape shape) {
if (shape instanceof ShapeCircle) {
ShapeCircle c = (ShapeCircle) shape;
double r = c.radius();
return Math.PI * r * r;
} else if (shape instanceof ShapeRectangle) {
ShapeRectangle r = (ShapeRectangle) shape;
double width = r.bottomRight().x() - r.topLeft().x();
double height = r.bottomRight().y() - r.topLeft().y();
return width * height;
}
throw new IllegalArgumentException();
}
// ===== 新写法(JDK16 Pattern Matching)=====
static double areaNew16(Shape shape) {
if (shape instanceof ShapeCircle c) {
return Math.PI * c.radius() * c.radius();
} else if (shape instanceof ShapeRectangle r) {
double width = r.bottomRight().x() - r.topLeft().x();
double height = r.bottomRight().y() - r.topLeft().y();
return width * height;
}
throw new IllegalArgumentException();
}
// ===== 最新写法(JDK21 Record Patterns + Switch)=====
static double area(Shape shape) {
return switch (shape) {
// Record Pattern解构:直接得到radius
case ShapeCircle(var center, var radius) ->
Math.PI * radius * radius;
// 嵌套Record Pattern解构:直接得到坐标值
case ShapeRectangle(Point(var x1, var y1), Point(var x2, var y2)) ->
Math.abs(x2 - x1) * Math.abs(y2 - y1);
};
}
// 计算两圆是否相交(展示嵌套解构的威力)
static boolean circlesIntersect(Shape s1, Shape s2) {
return switch (new Object[]{s1, s2}) {
// 这里不直接支持多参数,用一个小技巧
default -> {
if (s1 instanceof ShapeCircle(Point(var x1, var y1), var r1) &&
s2 instanceof ShapeCircle(Point(var x2, var y2), var r2)) {
double dist = Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
yield dist <= r1 + r2;
}
yield false;
}
};
}
// ===== 泛型Record Pattern =====
record Pair<A, B>(A first, B second) {}
record Triple<A, B, C>(A first, B second, C third) {}
static <A, B> void processPair(Object obj) {
if (obj instanceof Pair(var first, var second)) {
System.out.println("Pair: " + first + ", " + second);
}
}
static void processTypedPair(Object obj) {
if (obj instanceof Pair(Integer first, String second)) {
// 类型声明的Record Pattern:确保first是Integer,second是String
System.out.println("Int pair: " + (first * 2) + ", " + second.toUpperCase());
}
}
public static void main(String[] args) {
var shapes = List.of(
new ShapeCircle(new Point(0, 0), 5.0),
new ShapeRectangle(new Point(0, 0), new Point(4, 3))
);
for (var s : shapes) {
System.out.printf("面积: %.2f%n", area(s));
}
// 泛型Record Pattern
processPair(new Pair<>("hello", 42));
processTypedPair(new Pair<>(21, "jdk"));
processTypedPair(new Pair<>("not int", 42)); // 不匹配,跳过
}
}3.2 复杂业务场景:事件处理系统
import java.util.*;
import java.time.*;
/**
* Record Patterns在事件处理系统中的完整应用
*/
public class EventProcessingSystem {
// 事件体系
sealed interface AppEvent
permits UserEvent, OrderEvent, PaymentEvent, SystemEvent {}
record UserEvent(long userId, String action, Instant at) implements AppEvent {
// 嵌套的用户动作类型
sealed interface Action permits Login, Logout, ProfileUpdate {}
record Login(String ip, String device) implements Action {}
record Logout(String reason) implements Action {}
record ProfileUpdate(String field, String oldValue, String newValue) implements Action {}
}
record OrderEvent(String orderId, String status,
double amount, long userId) implements AppEvent {}
record PaymentEvent(String paymentId, String orderId,
double amount, String method) implements AppEvent {}
record SystemEvent(String component, String level,
String message) implements AppEvent {}
// 统计数据
record Stats(long count, double total, double max, double min) {
static Stats empty() { return new Stats(0, 0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); }
Stats merge(double value) {
return new Stats(count + 1, total + value, Math.max(max, value), Math.min(min, value));
}
double average() { return count == 0 ? 0 : total / count; }
}
// ===== 事件处理器(使用Record Patterns + Switch)=====
static String formatEvent(AppEvent event) {
return switch (event) {
// 基本Record Pattern
case UserEvent(var userId, var action, var at) ->
String.format("[USER] %s用户 %s at %s", userId, action, at);
// 带类型筛选的Pattern
case OrderEvent(var orderId, var status, var amount, var userId)
when amount > 1000 ->
String.format("[HIGH-VALUE ORDER] %s, 状态=%s, 金额=%.2f", orderId, status, amount);
case OrderEvent(var orderId, var status, var amount, var userId) ->
String.format("[ORDER] %s, 状态=%s, 金额=%.2f", orderId, status, amount);
case PaymentEvent(var paymentId, var orderId, var amount, var method) ->
String.format("[PAYMENT] %s, 订单=%s, %.2f via %s", paymentId, orderId, amount, method);
case SystemEvent(var component, var level, var message)
when "ERROR".equals(level) ->
String.format("[!!!ERROR] %s: %s", component, message);
case SystemEvent(var component, var level, var message) ->
String.format("[SYS-%s] %s: %s", level, component, message);
};
}
// 计算支付金额统计(演示嵌套处理)
static Stats calculatePaymentStats(List<AppEvent> events) {
var stats = Stats.empty();
for (var event : events) {
if (event instanceof PaymentEvent(var paymentId, var orderId, var amount, var method)) {
stats = stats.merge(amount);
}
}
return stats;
}
// 告警处理(多层嵌套解构)
static List<String> extractAlerts(List<AppEvent> events) {
var alerts = new ArrayList<String>();
for (var event : events) {
switch (event) {
case SystemEvent(var comp, "ERROR", var msg) ->
alerts.add("CRITICAL: [" + comp + "] " + msg);
case OrderEvent(var orderId, "CANCELLED", var amount, var userId)
when amount > 500 ->
alerts.add("HIGH-VALUE CANCELLATION: " + orderId + " ¥" + amount);
case PaymentEvent(var pid, var oid, var amount, var method)
when amount > 5000 ->
alerts.add("LARGE PAYMENT: " + pid + " ¥" + amount + " via " + method);
default -> {} // 无告警
}
}
return alerts;
}
public static void main(String[] args) {
var events = List.of(
new UserEvent(1001L, "login", Instant.now()),
new OrderEvent("O001", "PAID", 299.0, 1001L),
new OrderEvent("O002", "PAID", 1500.0, 1002L),
new OrderEvent("O003", "CANCELLED", 800.0, 1003L),
new PaymentEvent("P001", "O001", 299.0, "Alipay"),
new PaymentEvent("P002", "O002", 1500.0, "WeChat"),
new PaymentEvent("P003", "O002", 6000.0, "BankCard"),
new SystemEvent("OrderService", "ERROR", "数据库连接超时"),
new SystemEvent("GatewayService", "INFO", "服务启动")
);
System.out.println("=== 事件格式化 ===");
events.forEach(e -> System.out.println(formatEvent(e)));
System.out.println("\n=== 支付统计 ===");
var stats = calculatePaymentStats(events);
System.out.printf("笔数:%d, 总额:%.0f, 最大:%.0f, 平均:%.0f%n",
stats.count(), stats.total(), stats.max(), stats.average());
System.out.println("\n=== 告警列表 ===");
extractAlerts(events).forEach(System.out::println);
}
}3.3 嵌套解构的深度应用
/**
* Record Patterns嵌套解构的深度示例
* 模拟JSON类型系统的处理
*/
public class DeepNestingPatterns {
// 树形结构
sealed interface Tree<T> permits Tree.Leaf, Tree.Branch {}
record Tree.Leaf<T>(T value) implements Tree<T> {}
record Tree.Branch<T>(Tree<T> left, T value, Tree<T> right) implements Tree<T> {}
// ===== 旧写法:递归+类型检查 =====
static <T> int depthOld(Tree<T> tree) {
if (tree instanceof Tree.Leaf) {
return 0;
} else if (tree instanceof Tree.Branch) {
Tree.Branch<T> b = (Tree.Branch<T>) tree;
return 1 + Math.max(depthOld(b.left()), depthOld(b.right()));
}
throw new IllegalStateException();
}
// ===== 新写法:Record Pattern =====
static <T> int depth(Tree<T> tree) {
return switch (tree) {
case Tree.Leaf<T>(var value) -> 0;
case Tree.Branch<T>(var left, var value, var right) ->
1 + Math.max(depth(left), depth(right));
};
}
static <T> List<T> inorder(Tree<T> tree) {
return switch (tree) {
case Tree.Leaf<T>(var value) -> List.of(value);
case Tree.Branch<T>(var left, var value, var right) -> {
var result = new ArrayList<T>();
result.addAll(inorder(left));
result.add(value);
result.addAll(inorder(right));
yield result;
}
};
}
// 模式:仅处理特定形状的树(带嵌套Pattern的guard)
static <T extends Comparable<T>> boolean isBalanced(Tree<T> tree) {
return switch (tree) {
case Tree.Leaf<T> leaf -> true;
case Tree.Branch<T>(var left, var value, var right) -> {
int leftDepth = depth(left);
int rightDepth = depth(right);
yield Math.abs(leftDepth - rightDepth) <= 1
&& isBalanced(left) && isBalanced(right);
}
};
}
public static void main(String[] args) {
// 构建平衡二叉树: 4
// / \
// 2 6
// / \ / \
// 1 3 5 7
Tree<Integer> tree = new Tree.Branch<>(
new Tree.Branch<>(
new Tree.Leaf<>(1),
2,
new Tree.Leaf<>(3)
),
4,
new Tree.Branch<>(
new Tree.Leaf<>(5),
6,
new Tree.Leaf<>(7)
)
);
System.out.println("深度: " + depth(tree)); // 2
System.out.println("中序遍历: " + inorder(tree)); // [1, 2, 3, 4, 5, 6, 7]
System.out.println("平衡树: " + isBalanced(tree)); // true
// 不平衡的树
Tree<Integer> unbalanced = new Tree.Branch<>(
new Tree.Branch<>(
new Tree.Branch<>(new Tree.Leaf<>(1), 2, new Tree.Leaf<>(3)),
4,
new Tree.Leaf<>(5)
),
6,
new Tree.Leaf<>(7)
);
System.out.println("不平衡树深度: " + depth(unbalanced)); // 3
System.out.println("不平衡: " + isBalanced(unbalanced)); // false
}
}四、踩坑实录
坑1:解构时的类型必须精确匹配
record Pair<A, B>(A first, B second) {}
Object obj = new Pair<>("hello", 42);
// 错误1:类型不匹配(Integer不是String)
if (obj instanceof Pair(String first, String second)) { // 不会匹配
// 如果first是String但second是Integer,则不匹配
}
// 正确:使用var或匹配实际类型
if (obj instanceof Pair(var first, var second)) {
// first是String,second是Integer(Object视角)
System.out.println(first.getClass()); // String
}
// 或者精确匹配:
if (obj instanceof Pair(String first, Integer second)) {
System.out.println(first + ", " + second); // hello, 42
}坑2:null值不能被Record Pattern匹配
Object obj = null;
// instanceof对null总是false,Record Pattern也一样
if (obj instanceof Point(var x, var y)) {
// 永远不会执行
}
// null处理必须在Pattern之前
if (obj == null) {
System.out.println("null");
} else if (obj instanceof Point(var x, var y)) {
System.out.println(x + ", " + y);
}
// 或在switch里显式处理
switch (obj) {
case null -> System.out.println("null");
case Point(var x, var y) -> System.out.println(x + ", " + y);
default -> System.out.println("other");
}坑3:嵌套Pattern中间层不能命名
record Line(Point start, Point end) {}
record Point(int x, int y) {}
Object obj = new Line(new Point(0, 0), new Point(3, 4));
// 只解构end,需要同时访问整个line对象怎么办?
if (obj instanceof Line line
&& line.end() instanceof Point(var x2, var y2)) {
// 同时有line和x2, y2
System.out.println("Line length from origin to (" + x2 + "," + y2 + ")");
}
// 或者嵌套Pattern + 条件guard
// JDK21中,嵌套Pattern不能同时绑定中间层对象
// 必须先match到变量,再在另一个条件里解构坑4:性能:编译器优化情况
// Record Pattern解构在字节码层面是:
// 1. instanceof检查
// 2. 分别调用各accessor方法
// 编译器会优化:避免重复的instanceof检查
// 注意:和手动写accessor调用性能基本等同
// 没有额外开销
// 但是:深度嵌套Pattern会生成多个方法调用
// 对于性能极度敏感的热点代码,考虑手动解构后缓存
record Sensor(String id, Reading reading) {}
record Reading(double value, String unit) {}
// 这里nested pattern调用了3次方法
if (event instanceof Sensor(var id, Reading(var value, var unit))) {
// 等同于:
// var tmp = (Sensor) event;
// var id = tmp.id();
// var tmpR = tmp.reading();
// var value = tmpR.value();
// var unit = tmpR.unit();
}坑5:与JDK版本的混用问题
// 注意:Record Pattern是JDK21 GA的特性
// 在JDK16~20的代码里,只能用instanceof Pattern(不能解构)
// JDK16兼容写法:
if (event instanceof OrderEvent oe) { // JDK16+
double amount = oe.amount();
}
// JDK21新写法:
if (event instanceof OrderEvent(var orderId, var status, var amount, var userId)) { // JDK21+
// 直接用amount
}
// 如果项目还需要兼容JDK17(LTS),不能用Record Pattern
// 如果已经升到JDK21(LTS),大胆用五、总结与延伸
5.1 模式匹配体系全景
JDK16:instanceof Pattern(类型检查+绑定)
JDK21:
├── Record Pattern(类型检查+解构)
├── Switch Pattern Matching(switch表达式+类型Pattern)
├── Guard条件(when)
└── null处理
组合使用:
switch (obj) {
// 类型Pattern + 解构 + guard + 嵌套
case OrderEvent(var id, var status, var amount, var uid)
when amount > 1000 -> highValueProcess(id, amount);
case OrderEvent(var id, "CANCELLED", var amount, var uid) -> cancelProcess(id);
case OrderEvent oe -> normalProcess(oe);
// ...
}5.2 版本兼容建议
- JDK21(LTS):Record Patterns GA,可以在生产代码中使用
- 多JDK支持:如果需要同时支持JDK17(LTS),不能使用Record Patterns
- 迁移建议:升级到JDK21后,先在新代码里使用,然后逐步重构使用instanceof+手动字段访问的代码
