什么是密封类?它和 final、abstract 的区别是什么?
- 标签:Java、新特性
🏛 业务架构师
密封类(Sealed Classes,JDK 17 转正)让基类精确控制谁能为子类:sealed interface Payment permits Wechat, Alipay, Bank——限定实现集合,其余类想继承直接编译报错。
与 final、abstract 的区别:
- final:彻底关闭继承(零个子类);
- abstract:完全开放继承(任意子类);
- sealed:受控的中间态——白名单式继承,既保留多态扩展点,又锁死"谁能扩展"。
业务架构价值(这是它真正的主场):
- 领域建模的表达力:业务概念天然是有限集合——支付方式就那几种、订单状态机就那些状态、审批流的节点类型是封闭的。sealed 把这些"业务上封闭的分类"直接编码进类型系统,新增一种支付方式必须改 permits 声明——编译器强制所有 switch 处理新分支(配合模式匹配的穷尽性检查,漏处理直接编译失败)。以前新增枚举值漏改三处 if 的生产事故,从机制上消灭;
- 权限即设计:谁能扩展这个体系,是架构决策而不是口头约定——库作者/平台团队锁死扩展点,业务团队在许可范围内玩;
- 与 Record+模式匹配组合:sealed 接口 + record 实现 + switch 模式匹配 = Java 版代数数据类型(ADT),业务规则表达从 if-else 泥潭变成清晰的数据驱动结构。
2026 实践:状态机、领域事件类型、消息协议的命令/查询定义、结果类型(Success/Failure)——凡是"有限分类"的业务概念都建议 sealed 化。改造成本极低,换来的是长期可维护性保险。
🏗 系统架构师
密封类(Sealed Classes,JDK 17)= 精确控制继承边界的类:permits 白名单列出全部子类,白名单外禁止继承。
java
public sealed interface PaymentResult permits Success, Pending, Failed {}
public record Success(String txnId) implements PaymentResult {}
public record Pending(String redirectUrl) implements PaymentResult {}
public record Failed(int code, String message) implements PaymentResult {}与 final / abstract 的区别(控制力谱系):
- final:完全封闭——谁都不能继承,多态为零;
- abstract(普通):完全开放——任何人在任何包都能继承,多态集合开放无界;
- sealed:精确的中间态——"只允许这些子类",既有多态又封闭。这是继承控制从"全有/全无"到"按白名单"的粒度细化。
为什么它重要(架构视角的两个收益):
- 穷尽性 switch 的基石:sealed + record + pattern matching 三件套——编译器能验证 switch 覆盖全部子类,漏分支编译失败(default 不写则必须穷尽);新增子类时所有遗漏的 switch 处编译报错——把运行时风险前移到编译期,这是代数数据类型(ADT)思维进入 Java 的标志;
- 领域建模的表达力:封闭的"和类型"精确表达业务域的有限分类(支付结果、订单状态、事件类型)——相比"开放继承 + instanceof 散落"的老模式,类型即文档、变更即编译检查。
实战准则:领域内的有限变体 → sealed interface + record 实现(接口比 sealed class 灵活:record 可实现它);库的公开 API 扩展点 → sealed 保留演进权(外界只能用不能扩);确实要开放扩展 → 普通 abstract/interface。Sealed 是"默认封闭、显式开放"原则在语言层的落地——与 API 设计的安全原则一致。
💻 全栈开发专家
密封类(Sealed,JDK 17 正式):限定继承者的类——谁能扩展我,白纸黑字写在源码里。
java
public sealed interface Shape permits Circle, Square, Triangle { }
public record Circle(double r) implements Shape { } // 结合 record 很搭
public record Square(double a) implements Shape { }和 final / abstract 的区别(三档开放度):
| 修饰 | 谁能继承 | 语义 |
|---|---|---|
| final | 无人 | 完全封闭 |
| sealed | permits 列表内的类 | 白名单封闭(既知集合) |
| abstract/普通 | 任何人 | 完全开放 |
子类必须是 final、sealed、或 non-sealed(重新开放)三选一。
工程价值(我真实受益的两点):
- 穷尽性 switch 的基石:不用 default 分支,编译器保证覆盖所有子类——新增子类时所有相关 switch 编译报错,「漏改一处」的 bug 变成编译期错误:
java
double area = switch (shape) {
case Circle c -> Math.PI * c.r() * c.r(); // 模式匹配联动
case Square s -> s.a() * s.a();
case Triangle t -> heron(t);
}; // 若新增 Pentagon 且没处理,编译失败- 领域建模:状态机、支付渠道、消息类型这类「封闭集合」用 sealed 表达最准确——比 abstract(暗示随便继承)传达正确意图。
实战组合拳:sealed interface + record 实现 + switch 模式匹配 = 代数数据类型(ADT)的 Java 版——不可变数据 + 穷尽处理,我们支付网关的交易状态机重构就靠这套,删掉了 2000 行 if-else。