Java中sealed接口限制实现类需两步:接口用sealed+permits明确列出允许类;每个permits类必须用final、sealed或non-sealed显式声明继承策略,缺一不可,且需满足模块与包可见性要求。

Java 中用 sealed 接口限制具体实现类,核心是两步:接口声明时用 sealed + permits 明确列出允许的类;每个被列出的类必须用 final、sealed 或 non-sealed 显式声明自身继承策略。缺一不可,否则编译直接失败。
接口必须带 permits 列表
sealed 接口不能孤立存在,必须紧跟 permits 子句,后面跟逗号分隔的具体类名(大小写、包名全匹配):
- 写法示例:
public sealed interface PaymentStrategy permits CreditCardPayment, AlipayPayment {} - permits 后只能是已存在、可访问的类——不能是通配符、变量、表达式或未编译的类
- 如果类在其他模块,该模块必须在
module-info.java中导出对应包,且当前模块requires它 - 漏写 permits 或类名拼错(比如少个包前缀),编译器报错:
sealed type must have a permits clause或cannot find symbol
每个实现类必须声明修饰符
哪怕你只允许一个类实现,它也不能“默认自由”。所有出现在 permits 中的类,必须显式加 final、sealed 或 non-sealed:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
public final class CreditCardPayment implements PaymentStrategy—— 最常用,彻底封闭,不可继承 -
public sealed class AlipayPayment implements PaymentStrategy permits AlipayMock {}—— 自身也密封,可继续约束子类 -
public non-sealed class WechatPayment implements PaymentStrategy—— 开放继承,但仅限当前模块内(跨模块仍受模块系统限制) - 不加任何修饰符?编译器拒绝:
class X is not allowed to extend sealed interface Y
模块与包可见性不能忽略
sealed 的约束力依赖编译期校验,而校验前提是类型能被正确解析:
立即学习“Java免费学习笔记(深入)”;
- 接口和所有 permits 类最好放在同一模块;若跨模块,必须在提供方模块的
module-info.java中exports实现类所在包 - 调用方模块需
requires该模块;如需反射访问(如测试),还得额外加opens或opens to - 即使同包,不同模块默认不可见——JVM 不认“物理路径近”,只认模块声明
- 不导出实现类包,编译时找不到类;导出了但没 requires,同样报错
API 设计上要配合面向接口编程
sealed 的价值不仅在编译约束,更在运行时行为可控:
- 对外只暴露接口类型,工厂方法返回
PaymentStrategy,而不是CreditCardPayment - 避免文档、示例、构造器中暗示 new 具体实现类,防止使用者绕过契约
- 可把实现类设为私有静态嵌套类,进一步缩小作用域(如策略模式内部实现)
- 结合 switch 表达式可做穷尽匹配:
switch (strategy) { case CreditCardPayment c -> ...; case AlipayPayment a -> ...; },编译器确保覆盖全部 permits 类型

















