现代 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 适合表达透明的数据载体:编译器生成访问器、构造器、equals、hashCode 和 toString。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,把语言能力升级为可回滚的工程变更。