写点什么

Java 探索载体类以扩展面向数据编程

作者:A N M Bazlur Rahman
  • 2026-02-10
    北京
  • 本文字数:1991 字

    阅读完需:约 7 分钟

OpenJDK 的Amber项目发布了一份全新的设计说明,名为“Java面向数据编程:超越记录类(Record)”,阐述了一种探索性的方案,以便将类似记录类的特性拓展至更灵活的类设计中。该文档引入了载体类(carrier class)与载体接口(carrier interface)的概念,目标是提炼记录类的核心优势并进行通用化适配,同时不再强加严格的表述规则。

Java 16 引入了记录类,为不可变数据载体的建模提供了简洁的方式。如下这种记录类的声明:

record Point(int x, int y) { }
复制代码

编译器会自动为其生成规范的构造器、访问器方法,以及equalshashCodetoString方法的实现。记录类还支持解构(deconstruction)模式,可以配合instanceofswitch关键字使用。结合密封类与模式匹配特性,记录类能实现 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