record patterns 在 jdk 21 中仅支持全 record 嵌套结构的直观解构,依赖 canonical constructor 和 accessor;不支持普通类、null、泛型具体类型或跳级解构,且绕过构造器校验。

Record Patterns 在 JDK 21(正式特性,非预览)中真正让嵌套 record 解构变得直观可读——但前提是你的数据结构本身是 record,且嵌套层级明确;如果混用普通类、null 值或泛型擦除后的类型,模式会直接失败或静默退化。
record 模式匹配只对 record 类型生效,其他类型不支持
Java 的 record 模式(如 Point(var x, var y))底层依赖 canonical constructor 和公开的 accessor 方法。普通 class、sealed 类、甚至 enum 都无法参与该语法。
- ✅ 正确:嵌套结构全由
record构成,例如Order(record Customer(String name), List<item> items)</item> - ❌ 错误:把
Customer写成普通 class,哪怕它有 getter,Order(Customer c, ...)也无法在switch中用Order(Customer(var n), ...)解构 - ⚠️ 注意:
var在模式中不是类型推导,而是声明解构变量名;写成Order(Customer(String n), ...)是非法的——必须用var或显式类型 + 变量名,如Customer c
嵌套解构要逐层声明,不能跳级或省略中间层
Record 模式不支持“穿透式”解构(类似 Scala 的 @unchecked 或 Kotlin 的 destructuring declaration 跳过中间)。你必须显式写出每一层的 pattern,否则编译报错。
record Address(String city, String zip) {}
record Person(String name, Address address) {}
record Order(Person buyer, BigDecimal amount) {}
// ✅ 合法:三层完整解构
switch (order) {
case Order(Person(String n, Address(String c, String z)), BigDecimal amt) ->
System.out.printf("%s in %s (%s): %.2f%n", n, c, z, amt);
default -> throw new IllegalArgumentException();
}
// ❌ 编译错误:Address 没有被模式化,无法直接访问其字段
// case Order(Person p, BigDecimal amt) -> p.address().city() // 这不是 record pattern,只是普通调用
- 必须写
Person(String n, Address(String c, String z)),不能简写为Person(String n, String city) - 若某层 record 字段为
null,匹配会失败(抛IncompatibleClassChangeError或运行时NullPointerException,取决于上下文),需提前校验或用if预过滤 - 泛型 record 如
Box<t>(T value)</t>,模式中只能写Box(var v),无法指定T类型(类型擦除)
与 switch 表达式配合时,default 分支不可省略
Record patterns 用于 switch 时,JVM 要求穷尽所有可能的运行时类型。即使你只定义了 Order 一种 record,只要 switch 表达式的类型是 Object 或上界较宽(如 Serializable),就必须有 default 分支——编译器不认为 record pattern 能覆盖全部情况。
- ✅ 推荐:把 switch 参数类型声明为具体 record 类型(如
Order o),再配合 sealed hierarchy 可减少 default - ❌ 不要依赖 IDE 自动生成的 “missing case” 提示来判断是否安全——它只检查编译期已知子类,不验证运行时实际值
- 性能上无额外开销:record pattern 在字节码中翻译为常规字段访问和 instanceof 检查,和手写 if-chain 效率基本一致
最易忽略的一点:record pattern 不会触发任何构造逻辑或验证,它只是字段提取语法糖。如果你的 record 构造器里有业务校验(比如 Objects.requireNonNull(name)),pattern 匹配过程完全绕过这些——也就是说,一个通过反射或序列化构造出的非法 record 实例,也能被成功解构,后续使用才可能抛空指针。










