数据类与密封类
Kotlin 提供了两种强大的工具来构建数据模型:Data Class(持有数据)与 Sealed Class(持有状态/层级)。
数据类 (Data Class)
核心哲学:数据重于行为。编译器会自动从主构造函数中声明的所有属性导出以下成员:
equals()/hashCode()toString()格式为"User(name=John, age=42)"componentN()函数(用于解构)copy()函数
定义约束
- 主构造函数至少包含一个参数。
- 所有主构造参数必须标记为
val或var。 - 不能是
abstract,open,sealed或inner。
不可变性与 copy()
在多线程或响应式架构(如 MVI)中,不可变数据是核心。copy() 允许我们在修改部分属性的同时保持对象不可变。
kotlin
data class User(val id: Int, val name: String)
val user1 = User(1, "Bruce")
// 创建复本,只修改名字,id 保持不变
val user2 = user1.copy(name = "Virogu")解构声明 (Destructuring)
kotlin
val (id, name) = user1
println("ID: $id, Name: $name")顺序陷阱
解构是基于位置(顺序)的,而不是基于名称。 如果你调整了 data class 构造参数的顺序,解构逻辑可能会默默通过但赋值错误(如果类型兼容)。 建议:仅在局部且范围极小的代码块中使用解构。
密封类 (Sealed Class)
密封类用于表示受限的类层级。它本质上是一个枚举的扩展:枚举的实例是单例,而密封类的子类可以有多个实例且持有不同状态。
核心价值:穷举性 (Exhaustiveness)
当在 when 表达式中使用密封类时,编译器强制要求覆盖所有子类。这在 Android UI State 管理中至关重要。
kotlin
sealed interface UiState {
data object Loading : UiState // 单例使用 data object (Kotlin 1.9+)
data class Success(val data: List<String>) : UiState
data class Error(val msg: String) : UiState
}
fun render(state: UiState) {
// 编译器保证:如果新增了状态,这里会报错提醒修改
when (state) {
UiState.Loading -> showProgressBar()
is UiState.Success -> showList(state.data)
is UiState.Error -> showToast(state.msg)
}
}密封类 vs 密封接口 (Sealed Interface)
Kotlin 1.5 引入了密封接口。
| 特性 | Sealed Class | Sealed Interface |
|---|---|---|
| 状态持有 | 可包含具体属性/构造函数 | 不持有状态 (无构造函数) |
| 继承限制 | 单继承 | 多实现 |
| 类优化 | 类似于 Abstract Class | 纯接口 |
选型建议:
- 如果所有子类共用某些逻辑或属性 -> 用 Sealed Class。
- 如果仅仅是为了标记类型层级,或者子类已继承了其他父类 -> 用 Sealed Interface。
密封类 vs 枚举 (Enum)
kotlin
// 枚举:实例是常量,无法持有动态数据
enum class ConnectionState {
CONNECTING, CONNECTED, DISCONNECTED
}
// 密封类:实例可携带数据
sealed class Result
data class Success(val data: String) : Result() // 携带数据
object Failure : Result()