OpenJDK 的Amber项目发布了一份全新的设计说明,名为“Java面向数据编程:超越记录类(Record)”,阐述了一种探索性的方案,以便将类似记录类的特性拓展至更灵活的类设计中。该文档引入了载体类(carrier class)与载体接口(carrier interface)的概念,目标是提炼记录类的核心优势并进行通用化适配,同时不再强加严格的表述规则。
Java 16 引入了记录类,为不可变数据载体的建模提供了简洁的方式。如下这种记录类的声明:
record Point(int x, int y) { }编译器会自动为其生成规范的构造器、访问器方法,以及equals、hashCode和toString方法的实现。记录类还支持解构(deconstruction)模式,可以配合instanceof和switch关键字使用。结合密封类与模式匹配特性,记录类能实现 Java 中代数数据类型的建模。例如,HTTP 客户端或网关可以按如下方式定义不同的响应类型:
public sealed interface HttpResponse permits HttpResponse.Success, HttpResponse.NotFound, HttpResponse.ServerError { record Success(int status, String body) implements HttpResponse {} record NotFound(String message) implements HttpResponse {} record ServerError(int status, String error) implements HttpResponse {}}基于这样的响应类型层级,我们可以通过穷举式模式匹配进行统一处理:
static String handle(HttpResponse response) { return switch (response) { case Success(var code, var body) -> "OK (" + code + "): " + body; case NotFound(var msg) -> "404: " + msg; case ServerError(var code, var err) -> "Error (" + code + "): " + err; };}此示例中,编译器会强制检查是否覆盖了所有允许的响应类型。若新增一种响应类型,必须同步更新该 switch 表达式,从而降低不完整错误处理的风险。
在近期的一次讨论中,甲骨文公司的 Java 语言架构师Brian Goetz指出,这些特性的组合虽然能实现强大的数据建模能力,但实际落地却经常会受到长期形成的面向对象设计习惯制约。他发现,即便现代语言特性已能大幅减少间接代码,开发人员仍会习惯性地设计用于中介数据访问的 API。
这份设计说明重点聚焦于记录类无法适用的场景。实际开发中,许多数据类型需要派生值或缓存值、可选的内部表示形式、可变性或继承特性。在这种情况下,开发人员只能退而求其次,使用传统类,并重写大量的样板代码。文档将这种转变形容为“断崖式回落”,对记录类的基准模型做微小调整,就会导致代码量的大幅增加。
为了缓解这一问题,文档提出了载体类的设计思路。载体类以类似记录类头信息的状态描述作为开头,其余行为则与普通类完全一致:
class Point(int x, int y) { private final component int x; private final component int y;}其中,状态描述用于定义类的逻辑组件,编译器可基于这些组件自动生成访问器、对象方法及解构模式。与记录类不同,载体类不要求将所有状态仅存储在这些组件中,这也是其核心灵活性所在。
这种灵活性能实现记录类难以表达的设计模式,例如缓存派生值的场景:
class Point(int x, int y) { private final component int x; private final component int y; private final double norm; Point { norm = Math.hypot(x, y); } double norm() { return norm; }}此例中,派生值norm在构造阶段计算完成,并且未纳入状态描述,但该类仍能借助编译器为其组件自动生成的方法,减少样板代码编写。
载体类同样设计为可与模式匹配深度集成,用法与记录类一致:
if (obj instanceof Point(var x, var y)) { // use x and y}设计说明中还进一步探讨了载体类与未来重构特性的兼容性,例如,针对记录类的JEP 468。
除载体类外,该提案还引入了载体接口的概念,接口可声明自身的状态描述,并且所有实现类都能参与统一的模式匹配:
interface Pair<T, U>(T first, U second) { }switch (pair) { case Pair(var a, var b) -> ...}这种设计能简化日常开发中常见的元组式抽象,同时保留 Java 强类型的优势。
这份设计说明将载体类置于 Java 向面向数据编程整体转型的背景下,通过结合记录类、密闭类型、模式匹配,再加上潜在的载体类,Java 正逐步引导开发者直接建模数据结构,而非依赖层级繁杂的 API。Goetz 认为,当前的核心挑战在于,帮助开发者意识到,在将“数据”作为首要抽象时,大量的支撑性代码都可被省略。
目前,“超越记录类”还属于探索性的文档,官方尚未公布具体的语法定义、JEP 提案及版本发布时间表。但这份文档释放了明确的信号,那就是 Amber 项目将持续推进相关研发,进一步减少 Java 的样板代码,并将现代语言特性拓展至更复杂的类设计中,而这些探索,也许会将影响未来版本中 Java 开发者构建以数据为核心的 API 的方式。
原文链接:
Java Explores Carrier Classes to Extend Data-Oriented Programming Beyond Records





