密封类通过限定继承结构将运行时类型判断提前至编译期校验,要求顶层类型用sealed声明并一次性列出所有直接子类,子类须显式声明final/sealed/non-sealed,配合switch实现穷尽性检查,跨模块需同步模块声明确保白名单有效。

用密封类控制多层级类型强转,核心不是“强转”本身,而是让强转场景变得可预测、可验证——通过限定继承结构,把运行时类型判断提前到编译期校验。
明确密封边界:从顶层接口/抽象类开始声明
密封必须从最上层类型开始,且需一次性列出所有直接子类。不能留空、不能省略、不能靠注释说明。
- 接口或抽象类加 sealed,紧跟 permits 列出所有允许的实现类或子类(只写简单名,如
Circle, Rectangle) - 这些子类必须与密封父类在同一个模块;单模块项目中通常要求同包
- 父类可以是
abstract或具体类,但不能是final
逐层定义子类策略:final/sealed/non-sealed 不得遗漏
每个被 permits 允许的子类,必须显式声明自身是否继续封闭、彻底终止,还是开放分支。
-
final:终端类型,不再允许继承(如
final class Circle implements Shape) -
sealed:中间抽象层,自身也需带
permits(如sealed class Container permits Box, Bag) - non-sealed:主动开放该路径,允许任意后续继承(慎用,会破坏 switch 穷尽性)
配合 switch 实现类型安全的“隐式强转”
真正体现白名单价值的地方,是用 switch 替代传统 instanceof + 强转组合,让编译器替你检查是否覆盖全部合法类型。
- 变量类型必须精确为密封类型(如
Shape s,不能是Object或泛型擦除后的Serializable) - 每个
case必须对应一个permits中列出的直接子类(支持模式变量,如case Circle c -> c.radius) - 只要没用
non-sealed,编译器就能确认穷尽性,不需default分支
跨模块时补全模块声明:防止“白名单失效”
仅写 permits 不足以阻止外部模块非法实现——模块系统才是最后一道防线。
- 密封类所在模块,在
module-info.java中exports对应包(如exports com.example.shape;) - 子类所在模块,需
requires密封类模块,并在必要时opens自己的包(用于反射等场景) - 若子类本身也被其他模块使用,还需确保其包可见性同步开放
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











