本文目录导读:

可以,但有明确的限制条件。
Java 的密封接口(Sealed Interface)的核心目的之一就是为了限制哪些类可以实现该接口,但它的限制方式不同于使用 final(完全禁止)或默认的 public(完全开放)。
密封接口如何实现限制?
通过 permits 关键字,接口可以明确声明一个白名单。
// 定义一个密封接口,只允许三个指定的类实现它
public sealed interface Shape permits Circle, Rectangle, Triangle {
double area();
}
这表示: 除了 Circle、Rectangle、Triangle 这三个类之外,任何其他类都不能直接实现 Shape 接口。
限制的具体规则(三种允许的类型)
被 permits 声明的实现类(或子接口)本身必须满足以下三个条件之一:
a. final 类(彻底终止继承)
public final class Circle implements Shape {
// ...
}
- 限制效果: 这是最强的限制。
Circle不能再被任何类继承,也无法被其他类再次实现(因为它已经实现了)。
b. sealed 类/接口(进一步细化白名单)
public sealed class Rectangle implements Shape permits Square {
// ...
}
- 限制效果:
Rectangle本身不是最终形态,它允许Square来继承它。Square必须再通过final或sealed或non-sealed来结束这个链条。
c. non-sealed 类/接口(恢复为开放)
public non-sealed class Triangle implements Shape {
// ...
}
- 限制效果: 这是一个“例外”出口,如果你希望某个实现类允许其子类随意实现,可以使用
non-sealed,这相当于撤销了对Triangle及其子类的密封限制。
编译器强制检查
如果你尝试这样做:
// 编译错误!Pentagon 没有被 Shape 的 permits 列表包含
public class Pentagon implements Shape {
// ...
}
编译器会直接报错:
class is not allowed to implement the sealed interface Shape
为什么使用密封接口限制?(对比传统方式)
| 特性 | 传统接口(public interface) | 密封接口(sealed interface) |
|---|---|---|
| 实现者范围 | 谁都可以实现,无法限制 | 仅限 permits 列表中的类 |
| 维护性 | 新增实现类时,无法预料,可能导致不安全或不一致 | 受限的实现集合,便于模式匹配和逻辑穷举 |
| 未来可扩展性 | 无限扩展 | 需要明确修改 permits 才能扩展 |
典型应用场景
- 领域模型(Domain Model): 支付方式(
Payment)只有CreditCard、WeChatPay、Alipay三种。 - 语法树(AST): 表达式节点只有
Literal、BinaryOp、UnaryOp等。 - 结合
switch模式匹配: 使用密封类/接口后,switch可以检测是否穷举了所有子类型,不必再写default分支,编译器会帮助验证。
// 编译安全:编译器知道 Shape 只有 3 种实现
public String shapeName(Shape shape) {
return switch (shape) {
case Circle c -> "Circle";
case Rectangle r -> "Rectangle";
case Triangle t -> "Triangle";
// 不需要 default 分支
};
}
Java 密封接口确实能严格限制实现类,但不是通过“黑名单”模式,而是通过明确声明白名单 + 强制每个实现类声明自身状态(final/sealed/non-sealed)。
- 限制力: 强,编译器强制检查。
- 灵活性: 包含退路(
non-sealed),但需开发者主动声明。 - 价值: 提供更精确的接口契约控制,特别适合领域驱动设计(DDD)和模式匹配。