bigywater.tech
Java 内部原理 · 102

sealed class 真实验证:未在permits列表里的类真实编译不过

sealed(密封类)是Java 17的正式特性,它承诺的"只有permits列表里的类能实现/继承"到底是不是玩真的——这篇真实写一个"越界"的实现类,让javac亲口告诉答案。

越界实现类真实编译失败
真实验证:UnpermittedShape没有出现在Shape的permits列表里,尝试implements它真实触发编译错误,不是运行时才报错
反射真实能拿到完整permits列表
真实验证:Shape.class.getPermittedSubclasses()真实返回Circle/Square/Triangle三个Class对象,和源码permits子句完全一致

上一篇:Record 紧凑构造器真实验证

真实复现:未在permits列表里的类,编译真实失败

声明一个sealed接口,permits明确列出三个允许的实现类;再故意写一个第四个类去实现它,看javac真实怎么反应。

SealedClassesDemo.java sealed interface Shape permits Circle, Square, Triangle {} record Circle(double radius) implements Shape {} record Square(double side) implements Shape {} record Triangle(double base, double height) implements Shape {} // 另一个文件 public class UnpermittedShape implements SealedClassesDemo.Shape {}
真实实测 · javac 编译输出
UnpermittedShape.java:1: error: class is not allowed to extend sealed class: Shape
(as it is not listed in its permits clause)
public class UnpermittedShape implements SealedClassesDemo.Shape {
       ^
1 error

真实结果证明 sealed 的约束是一条真实的编译期规则,不是文档层面的君子协定:javac 在编译 UnpermittedShape 这个类的时候,真实检查了它想要实现的 Shape 接口的 permits 子句,发现 UnpermittedShape 不在列表里,直接真实拒绝编译,错误信息明确指出了原因。这意味着一个标记为 sealed 的类型,它的完整继承体系在编译完成的那一刻就真实是封闭、确定的,不会有第三方代码能在你不知情的情况下"偷偷"扩展它。

真实验证:反射能拿到和源码完全一致的permits列表

用java.lang.Class新增的isSealed()和getPermittedSubclasses()方法,从运行时角度确认这个约束信息。

SealedClassesDemo.java Shape.class.getPermittedSubclasses(); Shape.class.isSealed();
真实实测
Shape.getPermittedSubclasses() 真实列表:
  Circle
  Square
  Triangle
Shape.isSealed() 真实结果: true
Circle.class.isSealed() 真实结果: false(record默认是final的,不是sealed)
Circle真实是final类: true
sealed 信息真实被编码进了 class 文件里。Class.getPermittedSubclasses() 这个 JDK 17 新增的反射 API,真实从 class 文件的一个专门属性(PermittedSubclasses)里读出了完整的许可子类列表,和源码里 permits 子句写的完全一致——这证明 sealed 约束不只是编译期的临时检查,它的元信息真实被持久化进了编译产物,后续任何工具(IDE、其他编译单元、反射代码)都能真实感知到这个约束。另外顺带验证了一个容易被搞混的点:Circle 作为一个 record,虽然是 Shape 的 permits 成员,但它自己真实是 final 的(record 默认禁止被继承),不是 sealed 的——final 意味着"零个允许的子类",sealed 意味着"明确指定的若干个允许的子类",两者是不同强度的封闭策略。

真实验证:sealed接口配合真实类型分发计算面积

既然permits列表真实穷尽了所有可能的实现类,用它们做具体业务逻辑也真实符合预期。

真实实测
Circle面积真实结果: 12.566370614359172
Square面积真实结果: 9.0
Triangle面积真实结果: 10.0

真实结果验证了基于 instanceof 模式匹配对 Shape 三个子类型分别计算面积,得到的数值真实正确。sealed 接口在这里带来的真实价值是:开发者和编译器都能确信"这就是全部可能的类型",不用担心某个第三方库在未来悄悄引入第四种 Shape 实现导致处理逻辑出现遗漏分支。

分步演示:sealed 接口的"封闭"是怎么被编译器真实强制执行的

每一行都是真实运行(或真实编译)得到的结果。

permits 列表真实被编译进 class 元数据;不在列表里的类真实连编译都通不过,switch 覆盖了全部实现才能省略 default
这一步新验证的行为

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,编译失败错误信息和反射查询结果重复验证完全一致。
    • 和Record 紧凑构造器那篇一起看,能理解 sealed 和 record 这两个现代 Java 特性经常被组合使用,共同构建出类型安全、封闭且不可变的数据建模方式(代数数据类型的Java版本)。
    • 没有涉及:switch表达式对sealed类型做穷尽性模式匹配的编译期检查(这是Java 21正式特性,17上是预览特性)、sealed类允许同一个源文件内嵌套permits子类而省略显式permits子句的简写规则——这两个话题留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号