密封接口通过permits精确限定可信实现者,要求实现类为final或密封类,并结合模块封装与接口编程确保行为约束。

密封接口(sealed interface)在 Java 17+ 中用于限制实现类的范围,确保只有明确列出的类或接口才能实现它。要让“特定行为”只能被指定的密封组件实现,关键在于:用密封接口定义行为契约,用 permits 明确列出且仅列出可信的实现者,并配合访问控制与模块封装增强约束力。
用 permits 精确声明允许的实现者
密封接口必须通过 permits 子句显式列出所有直接实现类或子接口。JVM 会强制校验——任何未在 permits 列表中的类型尝试实现该接口,编译期即报错。
- 只写你真正信任并维护的实现类名,例如:permits PaymentProcessorImpl, MockPaymentService
- 避免使用通配符或动态加载类;permits 列表必须是编译期静态、可枚举的
- 若需扩展,必须修改接口源码并重新编译,天然形成“审批门禁”
将实现类设为 final 或密封类,防止进一步继承
即使接口已密封,若允许的实现类自身可被继承(如非 final),子类仍可能绕过行为限制。因此:
- 把每个 permits 列出的类声明为 final(最简单可靠)
- 或将其也设为密封类(sealed class X permits Y, Z),继续向下收敛
- 避免在实现类中暴露可重写的方法,尤其涉及核心行为逻辑的 public 方法
结合模块系统(module-info.java)隐藏实现细节
仅靠密封性不能阻止反射或跨模块非法访问。通过模块封装可强化限制:
- 在 module-info.java 中不导出(exports)实现类所在的包
- 仅导出密封接口所在包,供其他模块使用行为契约
- 必要时用 opens 或 opens to 有节制地开放反射权限(如测试需要)
在 API 层只暴露接口类型,不暴露具体实现
使用者应始终面向接口编程。工厂、服务定位器或依赖注入容器返回的必须是密封接口类型,而非具体实现类:
- 工厂方法签名应为 PaymentService createService(),而非 PaymentProcessorImpl createImpl()
- 避免在文档、示例或默认构造器中暗示或鼓励 new 具体实现类
- 可通过私有静态嵌套类实现,进一步缩小实现类可见性范围










