Java密封类Sealed Classes详解
1. 设计初衷
核心目标:允许类/接口的创建者精确控制继承关系,限制只有特定类能扩展或实现它们。
解决了那些问题?
• 传统Java继承模型过度开放,final 完全禁止继承,包级私有访问限制又无法跨包共享。
• 需明确表达“固定类型层次”的领域模型。
• 为模式匹配(如 switch 表达式)提供穷尽性检查的基础(编译器可验证所有子类覆盖)。
2. 使用语法
// 声明密封类/接口
public abstract sealed class Shape
permits Circle, Rectangle, Square {...} // 显式列出允许的子类
// 子类必须用以下修饰符之一:
public final class Circle extends Shape {...} // 禁止进一步扩展
public sealed class Rectangle extends Shape {...} // 继续密封(需指定子类)
public non-sealed class WeirdShape extends Shape {...} // 开放扩展(打破密封)
关键规则:
• permits 列表:子类必须在同一模块(或同一包),且需直接继承。
• 子类修饰符:子类必须显式声明 final、sealed 或 non-sealed。
• 省略 permits:若所有子类与密封类在同一源文件,可省略 permits(编译器自动推断)。
3. 面向对象中的应用场景
• 固定类型层次建模:例如图形库的 Shape 仅允许 Circle、Rectangle、Square 子类,避免未知子类破坏逻辑。
• 模式匹配配合:
// 编译器可验证覆盖所有密封子类(无需default)
return switch (shape) {
case Circle c -> c;
case Rectangle r -> r.rotate();
case Square s -> s.rotate();
};
• 记录类(Record)结合:
// 密封类 + 记录类 = 代数数据类型(乘积类型 + 求和类型),如:
sealed interface Expr permits ConstantExpr, PlusExpr {...}
record PlusExpr(Expr a, Expr b) implements Expr {...} // 记录类隐式final
4. 底层实现原理
1. 编译器验证:
• 强制检查子类是否符合 permits 列表、修饰符约束和访问规则。
• 转换检查:如 Shape 密封时,if (shape instanceof I) 可能编译报错(若无子类实现 I)。
2. JVM层支持:
• PermittedSubclasses 属性:在密封类的 .class 文件中存储允许的子类列表。
PermittedSubclasses_attribute {
u2 number_of_classes;
u2 classes[number_of_classes]; // 指向允许的子类
}
• 运行时拦截:JVM在加载子类时验证其是否在 PermittedSubclasses 列表中,否则抛出 IncompatibleClassChangeError。• 反射API扩展:
• Class.isSealed():判断类/接口是否密封。
• Class.getPermittedSubclasses():返回允许的子类数组(可能为空)。
更多推荐




所有评论(0)