permits 精准指定密封类的具象子类需满足三步闭环:显式声明白名单、编译期校验类存在性与继承关系、子类策略对齐(final/sealed/non-sealed),且仅接受简单类名、要求模块可见性。

要用 permits 精准指定哪些具象子类可以继承当前密封类,关键在于“显式声明 + 编译期校验 + 策略对齐”三步闭环。它不是运行时配置,也不是模糊约定,而是写死在源码里的白名单规则。
permits 列表必须是已定义、可访问的直接子类
列出的类名必须满足三个硬性条件:
- 已在当前编译单元中定义(或至少在当前模块中可见),不能是尚未写的占位符,比如不能写
permits Circle, Square却还没定义Square类 - 必须是该密封类的直接子类,间接子类(如
Square extends Rectangle)不能、也不需要出现在列表中 - 只能写简单类名(如
Circle),不能带包路径(com.example.Circle是非法的),也不能用通配符或正则表达式
每个被许可子类必须明确声明继承策略
只要进了 permits 名单,就自动触发强制约束:该子类自身必须用以下三者之一修饰,否则编译失败:
-
final class Circle extends Shape:彻底封死,不能再有子类,适合终端类型 -
sealed class Rectangle extends Shape permits Square:继续密封,但只允许指定下级子类,适合中间抽象层 -
non-sealed class Triangle extends Shape:主动开放此分支,任何类都可继承Triangle,但会削弱整体封闭性,慎用
密封关系必须在编码阶段就完整闭环
整个结构要像拼图一样严丝合缝:
- 父类声明
sealed并写全permits列表 - 列表中每个类都存在、可访问、且修饰符合法
- 若子类也是
sealed,它也必须有自己的permits子句,不能遗漏 - 跨模块使用时,还需模块系统配合:父类所在模块需
exports包,子类所在模块需requires该模块
常见错误与规避方式
这些写法都会导致编译报错,需特别注意:
- 写了
permits Dog, Cat,但Dog类没加final/sealed/non-sealed—— 编译器提示:“non-sealed, sealed, or final modifier expected” - 子类用了
non-sealed,却希望后续用switch做穷尽匹配 —— 此时编译器拒绝启用 exhaustive 检查,因为无法保证覆盖所有可能类型 - 把
permits类写在另一个包里,又没做模块导出 —— 报错:“class X is not allowed to extend sealed class Y”











