Record / SealedClass / PatternMatch 新特性源码
概述
JDK 16 起的三个语言特性——Record(数据载体)、Sealed Class(封闭层级)、Pattern Matching(模式匹配)——共同改变了 Java 表达数据结构的方式。它们在源码层面是语法糖,但在字节码与运行时层面有真实的支撑机制:
- Record 的
equals/hashCode/toString通过invokedynamic引导到java.lang.runtime.ObjectMethods; - Sealed Class 的
permits列表编译为 class 文件中的PermittedSubclasses属性,由 JVM 在类加载时强制校验; - switch 模式匹配生成类型检查 + 变量绑定的字节码序列。
本文基于 OpenJDK 21 源码,从编译产物到运行时 API 逐层拆解这三个特性的内部实现。
核心源码解析
① record Point(int x, int y) 的编译产物
record Point(int x, int y) 编译后等价于(源码层面):
java
public final class Point extends Record {
private final int x;
private final int y;
public Point(int x, int y) { this.x = x; this.y = y; } // 规范构造器
public int x() { return x; } // 访问器
public int y() { return y; }
@Override public boolean equals(Object o) { ... } // invokedynamic 引导
@Override public int hashCode() { ... }
@Override public String toString() { ... }
}- 父类
java.lang.Record(JDK 16 新增)是一个抽象类,仅声明三个抽象方法equals/hashCode/toString,本身不含任何字段:
java
public abstract class Record {
protected Record() {}
@Override public abstract boolean equals(Object obj);
@Override public abstract int hashCode();
@Override public abstract String toString();
}- 隐式成员生成:编译器为每个组件生成
private final字段、规范构造器、访问器(x()/y(),命名与字段名一致)、以及三个对象方法的实现。组件的顺序、名称、类型被记录在 class 文件(Record属性)中,供运行时反射使用。 - record 不能手动声明实例字段(仅静态字段允许)、不能扩展其他类、默认
final;x()与x的"同名访问器"是 Java 允许的特例。
② Record.toString() / equals() / hashCode() 的 invokedynamic
Record 的三个对象方法在字节码层面都是 invokedynamic 调用点,引导方法为 java.lang.runtime.ObjectMethods.bootstrap:
java
public final class ObjectMethods {
public static Object bootstrap(MethodHandles.Lookup lookup,
String methodName, // "equals" / "hashCode" / "toString"
TypeDescriptor type, // 方法的函数描述符
Class<?> recordClass, // record 类
String names, // 组件名称列表(逗号分隔)
MethodHandle... getters) { // 每个组件对应的访问器句柄
...
// 按 methodName 分发,用 getters 组装对应的 MethodHandle 实现
}
}MethodHandle实现:bootstrap根据methodName生成不同的句柄组合:toString:按Point[x=..., y=...]格式拼接组件;equals:先==比较,再instanceof检查,然后逐组件调用Objects.equals;hashCode:31 * h + component.hashCode()累加。
- 依赖组件名称和类型:
names字符串提供组件名(拼进toString输出),getters句柄提供取值路径——运行时不再需要反射,性能与手写等价。 - 单个
bootstrap方法复用:所有 record 类的三个方法都指向同一引导方法,JIT 可内联这些调用点。
③ Record.getRecordComponents() 的描述符
java
// Class.java
public RecordComponent[] getRecordComponents() {
return getReflectionFactory().getRecordComponents(this);
}ReflectionFactory.getRecordComponents(Class<?>) 读取 class 文件中的 Record 属性,构造 RecordComponent[]:
java
// java.lang.reflect.RecordComponent
public final class RecordComponent {
private final String name; // 组件名,如 "x"
private final Class<?> type; // 组件类型,如 int.class
private final Method accessor; // 访问器方法,如 Point.x()
private final String signature; // 泛型签名
private final Annotation[] annotations; // 组件上的注解(含类型注解)
}- 组件数组的顺序与
record声明顺序一致,可通过getRecordComponents()与Class.getDeclaredMethods()交叉验证。 - 反射框架(
getRecordComponents)与序列化框架(ObjectStreamClass对 record 的特殊处理)都依赖这份描述符完成按组件读写,而不是按字段名猜测。
④ sealed class Shape permits Circle, Rectangle 的编译
sealed 关键字在字节码层面落地为 PermittedSubclasses 属性(class 文件 17 版本):
Attribute "PermittedSubclasses":
u2 number_of_classes; // 被允许的直接子类数量
u2 classes[number_of_classes]; // 指向 CONSTANT_Class 的索引(Circle、Rectangle)- 编译器把
permits列表写入该属性;运行时通过ClassAPI 查询:
java
public boolean isSealed() {
return !isArray() && !isPrimitive() && getPermittedSubclasses0() != null;
}
public Class<?>[] getPermittedSubclasses() {
if (isArray() || isPrimitive() || isProxy()) return new Class<?>[0];
Class<?>[] permittedSubclasses = getPermittedSubclasses0(); // native 读取属性
if (permittedSubclasses == null) return new Class<?>[0];
return permittedSubclasses.clone();
}- 直接子类约束:
permits中列出的类必须是 sealed 类的直接子类(不能是孙类);未在permits中的直接子类会编译报错。 - sealed 类可以是
abstract或非abstract;final类本身隐含 sealed(无 permitted 子类)。 getPermittedSubclasses0()是 native 方法,直接解析类元数据中的属性,返回Class[]。
⑤ Class.permitsSubclasses() 的验证
类加载阶段的强制校验(HotSpot 的 SystemDictionary / Verifier 流程):
- 加载 sealed 类的直接子类时,JVM 读取父类的
PermittedSubclasses属性; - 校验当前类确实被父类 permits(即出现在父类的 permitted 列表中),否则抛
IncompatibleClassChangeError(实际为VerifyError或IncompatibleClassChangeError,视加载阶段而定); - 校验当前类与 sealed 类同模块同包(或同包、未命名模块);
- 通过后才允许
instanceof与继承关系成立。
java
// Class.java(实例化/继承相关的运行时反射)
public Class<?>[] getPermittedSubclasses() { ... } // 反射查询,返回 permits 列表- 反射与实例化校验:反射场景下
Class.forName/newInstance同样走类加载校验,无法绕过 sealed 约束。 - 目的是穷尽子类集合:编译器在 switch 模式匹配时利用 sealed 信息做穷尽性检查(
case覆盖所有 permitted 子类则无需default),这是 sealed 与模式匹配结合的关键编译期价值。
⑥ sealed + record + pattern matching 的联合使用
java
sealed interface Shape permits Circle, Rectangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
double area(Shape shape) {
return switch (shape) { // 穷尽:无需 default
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
};
}字节码层面的生成逻辑(javac):
- switch 先执行
shape.getClass()或instanceof链,按Shape的 permitted 子类逐一判断类型; - 命中分支后生成类型转换 + 模式变量绑定(
c/r是编译器合成的局部变量,来自 record 的访问器调用); - 因为
Shape是 sealed 且枚举了全部子类,编译器可证明穷尽,省略default分支不会报错。
运行时支撑:
- 类型判断走
Class.isInstance/instanceof(JVM 原生指令); - record 的访问器(
radius())是普通方法调用,JIT 内联后开销趋近于零; - 若未来新增一个
Shape的子类但忘记更新 switch,javac 会报"switch 不穷尽"——编译期安全,这正是 sealed 的意义。
⑦ Sealed Class 的安全限制
| 约束 | 说明 |
|---|---|
| 同模块 | permits 类必须与 sealed 类处于同一模块(named module 场景) |
| 同包 | 未命名模块场景下必须同一包 |
| 直接子类 | 只能是直接子类,不能跨级 |
| 声明顺序 | permits 列表顺序不影响运行时,但反射返回按属性顺序 |
| 运行时强制 | 违反约束在类加载阶段抛错,反射无法绕过 |
- 保证层级封闭:sealed 的核心收益是"可枚举的、封闭的子类集合"——安全相关代码(如穷尽性模式匹配、白名单校验)可以信任"全部可能类型"就在
permits中,无需防御性兜底。 - 与
final的区别:final禁止任何子类;sealed 允许明确列出的子类,且子类可继续向下扩展(除非子类自身也是 sealed/final)。 - 注意区分
javax.crypto.SealedObject(加密密封对象,用于序列化加密)——它与 sealed class 无关,只是同名。
总结
| 特性 | 编译期产物 | 运行时支撑 |
|---|---|---|
| Record | final class + 字段 + 访问器 + Record 属性 | java.lang.Record 基类 + ObjectMethods.bootstrap(invokedynamic) |
| Sealed Class | PermittedSubclasses 属性 | isSealed() / getPermittedSubclasses() + 类加载强制校验 |
| Pattern Matching | instanceof 链 + 类型转换 + 变量绑定字节码 | Class.isInstance + record 访问器内联 |
三个特性在编译期与运行时形成闭环:Record 提供不可变数据形态,Sealed 限定数据形态的封闭集合,Pattern Matching 用穷尽、类型安全的 switch 消费这些形态。理解 ObjectMethods 的 invokedynamic 引导与 PermittedSubclasses 的加载校验,就掌握了 JDK 现代 Java 语法的底层实现骨架。