1. 概述

在本篇教程中,我们将探讨如何在 Kotlin 中获取某个 sealed class(密封类)的所有子类。

这是一个在模式匹配、序列化或反射场景中非常实用的技巧。由于 sealed class 的特性,其所有子类在编译期就已知,这为我们进行静态分析和运行时处理提供了便利。

2. 获取 Sealed Class 的子类

我们知道,sealed classes 和 interfaces 是一种受限的继承结构:✅ 只有与密封类在同一文件或同一模块中的类才能继承它。这意味着编译器能够掌握该密封类的完整继承图谱。

举个例子:

sealed class Expr(val keyword: String)
class ForExpr : Expr("for")
class IfExpr : Expr("if")
class WhenExpr : Expr("when")
class DeclarationExpr : Expr("val")

上述代码定义了一个密封类 Expr,以及它的四个具体实现类。整个继承体系封闭且明确。

从 Kotlin 1.3 开始,我们可以通过标准库提供的反射 API 直接获取这些子类 —— 使用 KClass 上的 sealedSubclasses 属性:

val subclasses: List<KClass<*>> = Expr::class.sealedSubclasses

assertThat(subclasses).hasSize(4)
assertThat(subclasses).containsExactlyInAnyOrder(
  ForExpr::class, IfExpr::class, WhenExpr::class, DeclarationExpr::class
)

⚠️ 注意事项:

  • 必须通过 ::class 获取密封类的 KClass 实例
  • sealedSubclasses 返回的是 List<KClass<*>>,即子类的类对象列表
  • 子类顺序不保证,因此建议使用无序比对(如 containsExactlyInAnyOrder

这个方法在如下场景特别有用:

  • ✅ 枚举式逻辑分发(替代 or 扩展 when 表达式)
  • ✅ 序列化框架中自动注册支持类型
  • ✅ 编写测试用例时验证密封类完整性

❌ 常见踩坑点:

  • 忘记引入 kotlin-reflect 依赖,导致运行时抛出 UninitializedPropertyAccessException
  • 在 ProGuard/R8 混淆环境下未保留类信息,导致 sealedSubclasses 返回空列表

确保项目中已添加依赖:

implementation "org.jetbrains.kotlin:kotlin-reflect"

3. 总结

本文介绍了如何利用 Kotlin 反射 API 中的 sealedSubclasses 属性来获取 sealed class 的所有子类。

这一能力结合 sealed class 的封闭性,为编写类型安全、可扩展的代码提供了强大支持。尤其适合用于 DSL 设计、状态机建模或配置驱动逻辑等场景。

示例代码已托管至 GitHub:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-lang-oop-2


原始标题:Get All Subclasses of a Sealed Class in Kotlin