密封类通过sealed限定直接子类,仅对permits列出的子类允许使用non-sealed开放继承,customdocument作为拓展点需显式声明non-sealed,其子类才可继承,且须配合protected构造器、final方法和包级控制确保设计意图不被破坏。

用 sealed 和 non-sealed 留出可拓展口,核心不是“放开继承”,而是“在确定处精准解封”——只让指定子类成为合法扩展入口,其余分支保持受控。
明确密封起点与许可范围
先定义一个 sealed 抽象类,并用 permits 列出所有允许的直接子类。这一步锁定了继承的“第一层入口”,是后续所有控制的前提:
public abstract sealed class Document permits PdfDocument, HtmlDocument, CustomDocument { }- 这里
PdfDocument和HtmlDocument可设为final(固定实现),而CustomDocument就是你预留的拓展点
用 non-sealed 标记可扩展子类
被 permits 列出的子类,必须显式声明自己的继承策略。要开放拓展,就在该子类上加 non-sealed:
public non-sealed class CustomDocument extends Document { }- 这样任何模块、任何包里的类(只要可见性允许)都能写
class MyReport extends CustomDocument - 注意:不加
non-sealed会编译失败;写成public class CustomDocument extends Document(无修饰符)同样非法
配合访问控制强化拓展边界
non-sealed 解除的是继承限制,但不等于放任所有细节可被修改。需叠加其他机制守住设计意图:
- 把构造器设为
protected,避免外部随意实例化基类 - 关键方法用
final修饰,防止子类破坏核心逻辑 - 将
CustomDocument放在专用包中,并通过模块声明(exports)控制可见范围 - 在 JavaDoc 中明确标注:“本类为框架扩展点,建议仅重写
render()方法”
避免常见误用
几个容易踩的坑,直接影响拓展口是否真正可用:
-
non-sealed只对permits明确列出的子类有效,其他类即使加了也编译不过 - 它不传递:
CustomDocument是non-sealed,不代表它的子类自动能被再继承——那取决于子类自己怎么声明 - 不能跳级授权:你不能在
Document上直接写 “允许MyReport继承”,必须经由CustomDocument这一层中转











