密封类(sealed class)的特点与优势
1. 密封类的定义与语法
密封类是一种特殊的抽象类,用于表示受限的类层次结构。其子类必须在同一文件中声明(Kotlin 1.5 后允许在同一个编译单元的不同文件中声明)。语法如下:
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
2. 核心特点
- 受限的子类:所有子类在编译时已知,无法在密封类定义的文件外部添加新子类。
- 隐含的抽象性:密封类本身是抽象的,不能直接实例化。
- 与
when表达式的完美结合:编译器可以检查when是否覆盖所有可能情况,避免遗漏分支。
3. 主要优势
3.1 类型安全的状态管理
fun handleResult(result: Result<String>) {
when (result) { // 编译器强制要求处理所有子类
is Result.Success -> println(result.data)
is Result.Error -> println("Error: ${result.exception}")
Result.Loading -> println("Loading...")
}
}
✅ 优势:避免运行时因未处理的状态导致的错误。
3.2 替代枚举类的增强方案
与枚举类相比,密封类的子类可以:
- 携带不同的数据(如
Success带泛型数据,Error带异常) - 拥有独立的方法实现
3.3 模式匹配的编译期检查
val result: Result<Int> = Result.Success(42)
when (result) {
is Result.Success -> println(result.data * 2) // 智能类型转换
// 若漏写其他分支,编译器会警告
}
4. 典型应用场景
- 状态机建模:如网络请求状态(Loading/Success/Error)
- 表达式树:编译器中的 AST(抽象语法树)节点
- 事件处理:UI 交互事件的分类型处理
5. 与普通抽象类的对比
| 特性 | 密封类 | 普通抽象类 |
|---|---|---|
| 子类限制 | 必须同一文件/模块 | 可任意扩展 |
when 分支检查 | 编译器强制覆盖所有子类 | 无检查 |
| 使用场景 | 有限且固定的类型层次 | 需要扩展的类型层次 |
6. 最佳实践
- 优先用于状态管理:替代 Java 中的
interface + 多个实现类模式 - 结合数据类:子类使用
data class自动实现equals()/hashCode() - Kotlin 1.5+ 优化:利用
sealed interface进一步扩展设计灵活性
示例:实现一个文件下载状态机
sealed class DownloadState {
object Idle : DownloadState()
data class Progress(val percent: Int) : DownloadState()
data class Finished(val file: File) : DownloadState()
data class Failed(val error: Throwable) : DownloadState()
}
通过密封类,可以清晰地表达业务逻辑中的所有可能状态,并通过编译器的强制检查保证代码的健壮性。
