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