sealed类通过non-sealed为特定子类(如插件基类)显式授权扩展,需文档说明契约、限制实例化、隔离包级作用域,并保持模式匹配穷尽性,且non-sealed不向下传染。

在严控继承体系中,sealed 类本身不开放继承,但通过将某个子类明确标记为 non-sealed,就能在整体封闭的前提下,为特定分支保留合法、可预期的扩展能力——这不是妥协,而是分层治理的设计智慧。
只对有明确扩展意图的子类开放 non-sealed
非密封不是默认选项,而是显式授权。只有当某个子类承担“扩展锚点”角色时(如框架中的插件基类、领域模型中的可定制实体),才应使用 non-sealed。其他子类保持 final 或继续 sealed,确保绝大多数路径仍受控。
- 避免把
non-sealed加在具体实现类上(比如JsonSerializer),而应加在抽象基类上(如CustomizableSerializer) - 每个
non-sealed子类必须在文档中说明“谁可以扩展”“为什么能扩展”“扩展时需遵守什么契约” - 禁止在测试类、内部工具类等临时性代码中滥用
non-sealed
用包级封装 + 受限构造器约束扩展边界
即使允许继承,也不等于允许任意实例化。配合访问控制,能把弹性限制在合理范围内。
- 将
non-sealed类的构造器设为protected,防止跨包直接 new 实例 - 把该类放在专用扩展包(如
com.example.api.ext)中,与核心模块物理隔离 - 若需进一步限制,可在构造器中校验调用栈或 ClassLoader,仅允许已知插件加载器创建子类实例
延续密封语义:non-sealed 子类仍要参与模式匹配穷尽检查
Java 的 switch 表达式和 instanceof 模式匹配依赖编译期可知的类型集合。即使某分支是 non-sealed,只要它本身不是最终实现类,就不破坏穷尽性——因为匹配目标仍是密封父类的直接子类集合。
- 编译器只检查
Shape的permits列表(Circle/Rectangle/Polygon),不关心 Rectangle 是否被别人继承 - 你可以在
switch (shape)中覆盖全部三个直接子类,无需为RegularPolygon单独写 case - 若需要处理扩展子类,应通过多态方法(如
shape.render())而非类型匹配
避免 non-sealed 向下传染:不自动开放子类的子类
non-sealed 的作用范围仅限于**该类自身是否可被继承**,它不会让它的子类也变成非密封。这点常被误解。
-
non-sealed class Rectangle extends Shape→ 允许MyCustomRect extends Rectangle - 但
MyCustomRect默认仍是普通类,若不加修饰,它可被继承;若想终止,就加final - 没有隐式“继承 non-sealed 权限”的规则,每层继承权限都需显式声明










