bigywater.tech
Java · 224

Sealed Class 与反射:运行时如何看到允许的子类型

sealed 类型限定了谁能继承它。运行时怎么看到这个限定?编译期与运行期各自检查什么?新增子类后旧代码会怎样?这篇用反射、javap 和有意制造的版本不一致逐项验证。

3
Shape.getPermittedSubclasses() 返回的子类数
IncompatibleClassChangeError
加载违规子类时 JVM 抛出的错误
MatchException
旧 switch 遇到新增子类时抛出的异常

反射:isSealed 与 getPermittedSubclasses

实测 · 本机执行输出
  Shape.isSealed() = true,isInterface = true,修饰符 = public abstract interface
  Shape.getPermittedSubclasses() = [Circle, Square, Poly]
    Circle  isSealed=false final=true  record=true  修饰符=public final
    Square  isSealed=false final=true  record=false 修饰符=public final
    Poly    isSealed=false final=false record=false 修饰符=public
  非 sealed 类 Poly.getPermittedSubclasses() = null(null 表示不是 sealed);Tri 继承 non-sealed 的 Poly,Tri.isSealed() = false
  Shape.class.getPermittedSubclasses() 的顺序与源码 permits 的顺序:[Circle, Square, Poly](源码是 Circle, Square, Poly)

class 文件里的 PermittedSubclasses

实测 · 本机执行输出
  javap -v shapes.Shape 里的属性:
    PermittedSubclasses
    #8 = Class              #9             // shapes/Circle
    #9 = Utf8               shapes/Circle
    #10 = Class              #11            // shapes/Square
    #11 = Utf8               shapes/Square
  Circle 的 class 文件里有 PermittedSubclasses 吗: False ;Poly(non-sealed)里有吗: False

两层检查:javac 与 JVM

实测 · 本机执行输出
  javac:不在 permits 里的 Extra implements Shape → ["<jtmp>/j84b/shapes/Extra.java:1: error: class is not allowed to extend sealed class: Shape (as it is not listed in its 'permits' clause)"]
  javac:允许的子类 Square 没有写 final / sealed / non-sealed → ['<jtmp>/j84c/shapes/Square.java:1: error: sealed, non-sealed or final modifiers expected']
  运行时:Extra.class 声明继承 Shape,但新的 Shape.class 的 permits 里没有它 → IncompatibleClassChangeError: class shapes.Extra cannot implement sealed interface shapes.Shape

exhaustive switch 与重编译

实测 · 本机执行输出
  circle square poly
  旧的 App.class 遇到新子类 Hexagon → java.lang.MatchException: null
  App.name 的字节码里有 MatchException: True ;invokedynamic typeSwitch: True

分步演示

Sealed 与反射
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12
    • 没有验证:跨包与跨模块的 sealed 规则(同模块、同包的限制)、泛型 sealed 类型的穷尽性判断、record 模式的嵌套穷尽检查(见下一篇 switch 模式匹配)、sealed 与序列化、sealed 接口被 Proxy 代理时的表现
    京ICP备2026067502号-1京公网安备11010502063487号