Record 模式匹配(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
大翔(CTO):"小崔,咱们飞翔科技的订单审计系统最近处理日志的时候,是不是写了一堆
instanceof判断后再强转的代码?"小崔(后端开发):"是啊大翔,每个
OrderEvent都要先判断类型,再一个个getXxx()取字段,代码又臭又长。"白歌(架构师):"JDK 21 的 Record 模式匹配就是为解决这个问题而生的。你可以在
instanceof和switch里直接解构 Record,一行代码搞定类型判断和字段提取。"孔蓝(后端新人):"这么神奇?那嵌套的 Record 也能直接解构吗?"
什么是 Record 模式匹配
| 概念 | 说明 |
|---|---|
| Record 模式匹配 | 在 instanceof 或 switch 中直接解构 Record 对象,提取其组件值并绑定到变量 |
| 解构 | 将 Record 对象的组件值直接分解为多个变量的过程 |
| 模式变量 | 在模式中声明的变量,仅在匹配成功后才会初始化并可用 |
| 嵌套模式 | 在 Record 模式中嵌套另一个 Record 模式,实现深层解构 |
演进关系
基础语法:instanceof + Record 模式匹配
传统写法 vs Record 模式匹配
// === 场景说明 ===
// 传统写法:需要先 instanceof 判断,再强制类型转换,最后调用访问器方法
import java.math.BigDecimal;
import java.time.Instant;
record Point(int x, int y) {}
record Customer(String name, String email) {}
record OrderEvent(String orderId, BigDecimal amount, Instant timestamp, Customer customer) {}
public class RecordPatternDemo {
public static void main(String[] args) {
Object obj = new Point(10, 20);
// 传统写法
if (obj instanceof Point) {
Point p = (Point) obj;
int x = p.x();
int y = p.y();
System.out.println("点坐标: (" + x + ", " + y + ")");
}
// ✅ Record 模式匹配:直接解构
if (obj instanceof Point(int x, int y)) {
System.out.println("点坐标: (" + x + ", " + y + ")");
}
}
}
输出:
点坐标: (10, 20)
点坐标: (10, 20)
核心优势
| 对比维度 | 传统写法 | Record 模式匹配 |
|---|---|---|
| 代码行数 | 5-6 行 | 1 行 |
| 类型转换 | 需要强制转换 | 自动解构 |
| 变量作用域 | 外部作用域 | 模式内作用域 |
| 空安全 | 需手动检查 | 模式匹配保证 |
switch + Record 模式匹配
基础用法
// === 场景说明 ===
// 在 switch 表达式中使用 Record 模式匹配,根据 Point 的坐标值进行不同处理
public class RecordPatternDemo {
sealed interface Shape permits Circle, Rectangle, Point {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Point(int x, int y) implements Shape {}
public static String describeShape(Shape shape) {
return switch (shape) {
case Circle(double r) -> "圆形,半径: " + r;
case Rectangle(double w, double h) -> "矩形,宽: " + w + ", 高: " + h;
case Point(int x, int y) when x == y -> "点在对角线上: (" + x + ", " + y + ")";
case Point(int x, int y) -> "点: (" + x + ", " + y + ")";
};
}
public static void main(String[] args) {
System.out.println(describeShape(new Circle(5.0)));
System.out.println(describeShape(new Rectangle(3.0, 4.0)));
System.out.println(describeShape(new Point(5, 5)));
System.out.println(describeShape(new Point(3, 7)));
}
}
输出:
圆形,半径: 5.0
矩形,宽: 3.0, 高: 4.0
点在对角线上: (5, 5)
点: (3, 7)
流程图
嵌套 Record 模式匹配
深层解构
// === 场景说明 ===
// 嵌套 Record 模式匹配:直接解构 OrderEvent -> Customer -> Address 的嵌套结构
import java.math.BigDecimal;
import java.time.Instant;
record Address(String city, String street) {}
record Customer(String name, Address address) {}
record OrderEvent(String orderId, BigDecimal amount, Instant timestamp, Customer customer) {}
public class NestedRecordPattern {
public static String getOrderCity(OrderEvent event) {
// ✅ 嵌套解构:直接提取深层字段
if (event instanceof OrderEvent(String id, BigDecimal amt, Instant ts,
Customer(String name, Address(String city, String street)))) {
return city;
}
return "未知";
}
public static String processOrder(OrderEvent event) {
return switch (event) {
case OrderEvent(String id, BigDecimal amt, Instant ts,
Customer(String name, Address(String city, _)))
when city.equals("北京") -> "北京订单: " + id;
case OrderEvent(String id, BigDecimal amt, _, Customer(String name, _))
when amt.compareTo(new BigDecimal("10000")) > 0 -> "大额订单: " + id;
default -> "普通订单";
};
}
public static void main(String[] args) {
OrderEvent event = new OrderEvent(
"ORD-001",
new BigDecimal("5000"),
Instant.now(),
new Customer("张三", new Address("北京", "朝阳路"))
);
System.out.println("订单城市: " + getOrderCity(event));
System.out.println("订单处理: " + processOrder(event));
}
}
输出:
订单城市: 北京
订单处理: 北京订单: ORD-001
嵌套解构流程图
var 在模式匹配中的使用
类型推断
// === 场景说明 ===
// 使用 var 进行类型推断,让编译器自动推断 Record 组件的类型
import java.math.BigDecimal;
import java.time.Instant;
record Point(int x, int y) {}
record OrderEvent(String orderId, BigDecimal amount, Instant timestamp, String status) {}
public class RecordPatternDemo {
public static void main(String[] args) {
Object obj = new Point(10, 20);
// ✅ 使用 var:编译器自动推断类型
if (obj instanceof Point(var x, var y)) {
System.out.println("点坐标: (" + x + ", " + y + ")");
// x 和 y 的类型被推断为 int
}
Object event = new OrderEvent("ORD-001", new BigDecimal("999.99"),
Instant.now(), "PAID");
// ✅ switch + var:灵活处理不同类型
String result = switch (event) {
case OrderEvent(var id, var amount, var time, var status)
when status.equals("PAID") -> "已支付订单: " + id;
case Point(var x, var y) -> "点: (" + x + ", " + y + ")";
default -> "未知类型";
};
System.out.println(result);
}
}
输出:
点坐标: (10, 20)
已支付订单: ORD-001
var 的优势
| 场景 | 优势 |
|---|---|
| 复杂类型 | 避免手写冗长的泛型类型 |
| 代码重构 | Record 组件类型变更时无需修改模式 |
| 可读性 | 聚焦业务逻辑而非类型声明 |
实战场景:飞翔科技订单审计系统
业务背景
飞翔科技的订单审计系统需要处理海量订单事件,识别异常订单并进行实时告警。
// === 场景说明 ===
// 实战:订单审计系统中的异常检测,使用 Record 模式匹配直接解构订单事件
import java.math.BigDecimal;
import java.time.Instant;
import java.util.List;
record Address(String city, String street) {}
record Customer(String name, String level, Address address) {}
record OrderEvent(String orderId, BigDecimal amount, Instant timestamp,
Customer customer, String eventType) {}
public class OrderAuditSystem {
// 审计日志处理:直接解构 OrderEvent
public static void processAuditLog(Object logEntry) {
if (logEntry instanceof OrderEvent(String orderId, BigDecimal amount,
Instant timestamp, Customer customer,
String eventType)) {
System.out.println("审计日志 - 订单ID: " + orderId);
System.out.println(" 金额: " + amount);
System.out.println(" 客户: " + customer.name());
System.out.println(" 事件类型: " + eventType);
}
}
// 异常检测:嵌套解构 + 模式匹配
public static String detectAnomaly(OrderEvent event) {
return switch (event) {
// 大额订单且客户为 VIP
case OrderEvent(var id, var amount, _,
Customer(var name, "VIP", _), "CREATE")
when amount.compareTo(new BigDecimal("50000")) > 0
-> "⚠️ 高危: VIP客户大额新订单 - " + id;
// 异地订单
case OrderEvent(var id, _, _,
Customer(_, _, Address(city, _)), "CREATE")
when !city.equals("北京") && !city.equals("上海")
-> "⚠️ 注意: 异地订单 - " + id;
// 取消的已支付订单
case OrderEvent(var id, var amount, _, _, "CANCEL")
when amount.compareTo(new BigDecimal("1000")) > 0
-> "⚠️ 损失: 大额已支付订单取消 - " + id;
default -> "✅ 正常";
};
}
public static void main(String[] args) {
List<OrderEvent> events = List.of(
new OrderEvent("ORD-001", new BigDecimal("60000"), Instant.now(),
new Customer("张三", "VIP", new Address("北京", "朝阳路")), "CREATE"),
new OrderEvent("ORD-002", new BigDecimal("3000"), Instant.now(),
new Customer("李四", "NORMAL", new Address("成都", "天府大道")), "CREATE"),
new OrderEvent("ORD-003", new BigDecimal("2000"), Instant.now(),
new Customer("王五", "NORMAL", new Address("上海", "南京路")), "CANCEL")
);
for (OrderEvent event : events) {
processAuditLog(event);
System.out.println(" 异常检测: " + detectAnomaly(event));
System.out.println("---");
}
}
}
输出:
审计日志 - 订单ID: ORD-001
金额: 60000
客户: 张三
事件类型: CREATE
异常检测: ⚠️ 高危: VIP客户大额新订单 - ORD-001
---
审计日志 - 订单ID: ORD-002
金额: 3000
客户: 李四
事件类型: CREATE
异常检测: ⚠️ 注意: 异地订单 - ORD-002
---
审计日志 - 订单ID: ORD-003
金额: 2000
客户: 王五
事件类型: CANCEL
异常检测: ⚠️ 损失: 大额已支付订单取消 - ORD-003
---
系统架构流程图
record + sealed class + switch 三件套
组合威力
// === 场景说明 ===
// record + sealed class + switch 模式匹配:构建类型安全的领域模型
import java.math.BigDecimal;
// sealed class 定义封闭的继承层次
sealed interface Payment permits WeChatPay, Alipay, BankCardPay, ApplePay {}
// record 定义不可变数据载体
System.out.println(processPayment(p1));
System.out.println(processPayment(p2));
System.out.println(processPayment(p3));
System.out.println(processPayment(p4));
}
}
输出:
微信支付 [交易ID: WX123456, 金额: 199.99]
支付宝支付 [交易号: ALI789012, 金额: 299.50]
银行卡支付 [卡号后4位: 7890, 银行: 工商银行, 金额: 999.00]
Apple Pay [Token: AP-Pay..., 金额: 499.00]
三件套威力分析
| 特性 | 作用 | 优势 |
|---|---|---|
| sealed class | 限制继承层次 | 编译器保证 switch exhaustive |
| record | 不可变数据载体 | 自动生成 equals、hashCode、toString,支持解构 |
| switch 模式匹配 | 模式匹配 + 解构 | 一行代码完成类型判断和字段提取 |
与未命名模式 _ 的结合
初步使用
// === 场景说明 ===
// 使用未命名模式 _ 忽略不需要的 Record 组件
import java.math.BigDecimal;
import java.time.Instant;
record OrderEvent(String orderId, BigDecimal amount, Instant timestamp, String status) {}
public class UnnamedPatternDemo {
public static void main(String[] args) {
OrderEvent event = new OrderEvent("ORD-001", new BigDecimal("999.99"),
Instant.now(), "PAID");
// ✅ 使用 _ 忽略不需要的组件
if (event instanceof OrderEvent(var id, _, _, "PAID")) {
System.out.println("已支付订单: " + id);
}
// ✅ switch 中使用 _ 忽略组件
String result = switch (event) {
case OrderEvent(var id, _, _, "PAID") -> "订单已支付: " + id;
case OrderEvent(var id, _, _, "CANCELLED") -> "订单已取消: " + id;
case OrderEvent(var id, var amount, _, _)
when amount.compareTo(new BigDecimal("1000")) > 0 -> "大额订单: " + id;
default -> "其他状态";
};
System.out.println(result);
}
}
输出:
已支付订单: ORD-001
订单已支付: ORD-001
注意:未命名模式
_在 JDK 21 中作为预览特性引入,需要在编译和运行时使用--enable-preview参数。详细用法将在《未命名模式》文档中展开。
易错场景
1. 模式变量作用域错误
// === 场景说明 ===
// 错误:在模式匹配外部使用模式变量
public class RecordPatternError {
public static void main(String[] args) {
Object obj = new Point(10, 20);
// ❌ 错误:模式变量 x, y 仅在 if 块内有效
if (obj instanceof Point(int x, int y)) {
System.out.println("点坐标: (" + x + ", " + y + ")");
}
// System.out.println(x); // 编译错误:x 找不到符号
// ✅ 正确:在模式匹配块内使用模式变量
if (obj instanceof Point(int x, int y)) {
System.out.println("点坐标: (" + x + ", " + y + ")");
int sum = x + y; // 正确:在作用域内使用
System.out.println("坐标和: " + sum);
}
}
}
2. switch 模式匹配顺序错误
// === 场景说明 ===
// 错误:switch 分支顺序不当,导致代码不可达
record Point(int x, int y) {}
public class RecordPatternError {
public static String describePoint(Point p) {
// ❌ 错误:具体条件分支应放在前面
return switch (p) {
case Point(int x, int y) -> "点: (" + x + ", " + y + ")";
// 下面这行代码不可达,编译错误
// case Point(int x, int y) when x == y -> "点在对角线上";
};
// ✅ 正确:具体条件分支放在前面
return switch (p) {
case Point(int x, int y) when x == y -> "点在对角线上: (" + x + ", " + y + ")";
case Point(int x, int y) -> "点: (" + x + ", " + y + ")";
};
}
}
3. 嵌套解构类型不匹配
// === 场景说明 ===
// 错误:嵌套解构时类型不匹配
record Inner(int value) {}
record Outer(Inner inner) {}
public class RecordPatternError {
public static void main(String[] args) {
Object obj = new Outer(new Inner(42));
// ❌ 错误:嵌套解构类型不匹配
// if (obj instanceof Outer(String s)) { // 编译错误:Inner 不能匹配 String
// System.out.println(s);
// }
// ✅ 正确:嵌套解构类型匹配
if (obj instanceof Outer(Inner(int value))) {
System.out.println("内部值: " + value);
}
// ✅ 正确:使用 var 进行类型推断
if (obj instanceof Outer(Inner(var v))) {
System.out.println("内部值: " + v);
}
}
}
面试考点
问题 1:Record 模式匹配相比于传统的 instanceof + 强制类型转换有哪些优势?
答案:
代码简洁:传统写法需要 5-6 行代码(instanceof 判断 + 强制转换 + 调用访问器方法),而 Record 模式匹配只需 1 行。
作用域安全:模式变量的作用域仅限于匹配成功的块内,避免了变量泄露到外部作用域的风险。
类型安全:编译器在编译时检查类型匹配,避免了运行时
ClassCastException的风险。空安全:模式匹配保证只有在类型匹配成功时才会解构,避免了
NullPointerException。可读性强:一眼就能看出代码意图,直接看到解构后的变量名。
问题 2:record + sealed class + switch 三件套组合有什么威力?为什么它们被称为"三件套"?
答案:
这三者的组合被称为"三件套",因为它们共同构建了一个类型安全的领域模型:
sealed class:限制继承层次,编译器知道所有子类,保证
switch表达式是 exhaustive 的(无需default分支)。record:不可变数据载体,自动生成
equals、hashCode、toString,最重要的是支持解构,可以在模式匹配中直接提取组件值。switch 模式匹配:在
switch中直接使用模式匹配,一行代码完成类型判断和字段提取,编译器进行 exhaustiveness 检查。威力体现:
- 编译时类型安全检查,无需运行时类型判断
- 无需
default分支,新增子类时编译器会提示补充case- 代码极其简洁,表达力强
sealed interface Expr permits Add, Mul, Num {} record Add(Expr left, Expr right) implements Expr {} record Mul(Expr left, Expr right) implements Expr {} record Num(int value) implements Expr {} int eval(Expr e) { return switch (e) { case Num(int v) -> v; case Add(Expr l, Expr r) -> eval(l) + eval(r); case Mul(Expr l, Expr r) -> eval(l) * eval(r); }; }
问题 3:嵌套 Record 模式匹配的解构过程是怎样的?有什么注意事项?
答案:
解构过程:
- 外层 Record 类型匹配成功
- 编译器自动调用外层 Record 的访问器方法获取内层 Record
- 对内层 Record 进行类型匹配和解构
- 将所有解构出的变量绑定到模式变量
注意事项:
类型必须匹配:嵌套解构时,内层类型必须与实际类型匹配。
可以使用
_忽略组件:不需要的组件可以用_忽略。可以使用
var推断类型:不确定类型时可以使用var让编译器自动推断。解构深度没有限制:可以嵌套多层解构,但建议不要超过 3 层,否则影响可读性。
// 多层嵌套解构 if (obj instanceof OrderEvent(var id, var amount, _, Customer(var name, _, Address(var city, _)))) { System.out.println("订单: " + id + ", 客户: " + name + ", 城市: " + city); }
总结
Record 模式匹配是 JDK 21 中的一个重要特性,它让 Java 在模式匹配方面迈出了重要一步。通过 instanceof 和 switch 中的直接解构,我们可以写出更简洁、更安全的代码。
结合 sealed class 和 record,我们可以构建出类型安全的领域模型,让编译器帮助我们进行类型检查,减少运行时错误。
白歌(架构师):"Record 模式匹配不仅仅是语法糖,它改变了我们处理数据的方式。从繁琐的类型判断和强制转换,到简洁的模式匹配和解构,这是 Java 语言现代化的重要一步。"
大翔(CTO):"没错,而且在飞翔科技的订单审计系统中,使用 Record 模式匹配后,代码量减少了一半,可读性也大大提升。这就是技术进步带来的实际价值。"
孔蓝(后端新人):"我现在明白了!以后处理复杂的数据结构,我会优先考虑使用 Record + 模式匹配的组合。"
小崔(后端开发):"我要赶紧把订单审计系统的代码重构一下,用上这些新特性!"
编程名言:"Simplicity is the soul of efficiency." — Austin Freeman
简约是效率的灵魂。