跳到正文
Elaine Blog
返回

Record、密封类与模式匹配

Java 基础

现代 Java 可以把“业务状态只有这几种”直接写进类型系统。这样新增状态时,编译器会指出哪些分支尚未处理,而不是等到 生产流量命中默认分支。

Table of contents

Open Table of contents

用密封层次表达支付结果

sealed interface 支付结果 permits 支付成功, 支付失败, 支付处理中 {}

record 支付成功(String transactionId) implements 支付结果 {}
record 支付失败(String code, String message) implements 支付结果 {}
record 支付处理中(String queryToken) implements 支付结果 {}

static String 展示(支付结果 result) {
    return switch (result) {
        case 支付成功(var id) -> "支付成功,流水号=" + id;
        case 支付失败(var code, var message) -> "支付失败:" + code + "" + message;
        case 支付处理中(var token) -> "处理中,查询凭证=" + token;
    };
}

**密封类型(Sealed Type)**限制允许实现它的类型集合。switch 能检查所有可能分支,因此这里不需要掩盖遗漏的 default。**模式匹配(Pattern Matching)**同时检查类型并提取 Record 组件。

Record 不是自动不可变一切

Record 适合表达透明的数据载体:编译器生成访问器、构造器、equalshashCodetoString。Record 的字段引用 不可重新赋值,但引用指向的可变集合仍然能变化。

record 订单(String id, java.util.List<String> items) {
    订单 {
        if (id == null || id.isBlank()) throw new IllegalArgumentException("订单号不能为空");
        items = java.util.List.copyOf(items);
    }
}

防御性复制后,调用方无法通过原列表修改订单内容。Record 不适合需要代理继承的实体、隐藏内部表示的复杂对象,或字段 会逐步变化的生命周期模型。

选择 enum 还是密封层次

状态只有名称、所有状态携带相同数据时用 enum。不同状态携带不同数据或行为时,密封接口更自然。例如“成功”需要流水 号,“失败”需要错误码,硬塞进一个类会产生大量只在部分状态有效的可空字段。

版本边界

Record 在 Java 16 正式发布,密封类在 Java 17 正式发布,Record Pattern 和 switch 模式匹配在 Java 21 正式发布。 如果库需要兼容 Java 17,就不能在已发布 API 中直接使用 Java 21 字节码。用 Maven release 或 Gradle Toolchain 明确 最低版本,并在 CI 使用最低 JDK 运行测试。

下一步

阅读从 Java 17 迁移到 21/25,把语言能力升级为可回滚的工程变更。


分享这篇文章:

上一篇
JUnit 与 Mockito:测试替身怎么选
下一篇
从 Java 17 迁移到 21/25