Java 17密封类通过sealed+permits实现编译期继承白名单控制,子类须显式声明final/sealed/non-sealed,配合switch表达式提供穷举保障,跨模块需注意模块导出与可见性约束。

Java 17 引入的密封类(Sealed Classes)机制,可以精准限制哪些类能继承某个抽象类,实现真正的“继承白名单”控制——不再依赖文档约定或运行时检查,而是由编译器强制保障。
用 sealed + permits 明确声明允许的子类
在抽象类定义时使用 sealed 修饰符,并通过 permits 列出所有合法直接子类(必须是同一编译单元内,或已明确模块导出):
public abstract sealed class Shape permits Circle, Rectangle, Triangle { ... }
此时,只有 Circle、Rectangle 和 Triangle 可以继承 Shape;其他类继承会触发编译错误。注意:permits 中列出的每个子类也必须声明自己的封印状态(如 final、sealed 或 non-sealed)。
为每个子类选择合适的封印策略
允许继承不等于放任扩展。每个 permits 子类需显式声明其自身是否可被进一步继承:
立即学习“Java免费学习笔记(深入)”;
- final class Circle extends Shape:彻底封闭,不可继承
- sealed class Rectangle extends Shape permits Square:仅允许 Square 继承
- non-sealed class Triangle extends Shape:开放继承,任何类都可继承 Triangle
这种分层控制让抽象类的继承边界既严格又灵活,避免“全开”或“全关”的粗粒度限制。
配合 switch 表达式实现完备的模式匹配
密封类天然适配 Java 17+ 的 switch 表达式。编译器能验证是否覆盖了所有允许的子类型,杜绝遗漏分支:
String describe(Shape s) {
return switch (s) {
case Circle c -> "Circle with radius " + c.radius();
case Rectangle r -> "Rectangle " + r.width() + "x" + r.height();
case Triangle t -> "Triangle with sides " + t.sides();
};
}
如果后续新增子类但未更新 switch,编译直接报错——这是类型安全的“穷举保障”,比 instanceof 链更可靠、更简洁。
跨文件/模块时注意 permits 的可见性约束
若子类不在同一源文件中,permits 列表仍有效,但需确保:
- 所有 permits 类型必须已声明(编译期可解析)
- 在模块化项目中,父类所在模块需 exports 包,且子类模块需 requires 该模块
- 若子类位于不同模块,父类声明需改为 sealed 且不写 permits,改由子类模块在 module-info.java 中用 opens 或 exports 配合 permits 声明(较复杂,建议同模块优先)
实际项目中,把密封类及其子类放在同一模块和合理包结构下,最易维护且编译检查最稳定。


















