数据类与密封类
本教程共 100 篇 · 第 14 篇 · 更新于 2026-07-28 · 约 7 分钟阅读
14. 数据类与密封类
本节目标:掌握 data class、sealed class、enum class、object 这四种 Kotlin 特色类,知道它们各自适合什么场景,尤其在 Android 状态管理里怎么用。
数据类(data class)
存数据的类用 data class,一行声明,自动生成一堆实用方法:
data class User(val name: String, val age: Int, val email: String = "")
自动生成的有:
equals()/hashCode():按属性值比较。toString():格式User(name=张三, age=18, email=...)。copy():复制对象,可改部分属性。componentN():解构声明用。
val u1 = User("张三", 18)
val u2 = User("张三", 18)
println(u1 == u2) // true,按值比较
val u3 = u1.copy(age = 20) // 改年龄,其他不变
val (name, age, email) = u1 // 解构声明
限制
- 主构造至少要有一个
val/var参数。 - 不能是
abstract、open、sealed、inner。 - 数据类非常适合做模型层(网络响应、数据库实体)。
Tip
- Android 里网络响应实体、Room 数据库 Entity、Compose 状态,首选
data class。它默认不可变(属性都用val),并发安全。
解构声明
data class 自动支持解构,把对象拆成多个变量:
data class Point(val x: Int, val y: Int)
val p = Point(10, 20)
val (x, y) = p
println("$x, $y") // 10, 20
// 在 for 循环里
val points = listOf(Point(1, 2), Point(3, 4))
for ((x, y) in points) {
println("$x, $y")
}
Map 的 entry 也能解构:for ((k, v) in map)。
密封类(sealed class)
Android 状态管理的核心工具。密封类是一种受限的类层级,所有子类必须定义在同一个文件(或同一模块的同一包),编译器能知道所有可能子类。
sealed class UiState {
object Loading : UiState() // 加载中
data class Success(val data: List<Item>) : UiState() // 成功
data class Error(val message: String) : UiState() // 失败
}
配合 when 使用
密封类最大的好处是 when 配合时编译器会强制你处理所有分支,漏掉一个编译报错:
fun render(state: UiState) {
when (state) {
is UiState.Loading -> showLoading()
is UiState.Success -> showData(state.data) // is 后自动智能转换
is UiState.Error -> showError(state.message)
// 不需要 else,因为编译器知道就这三种
}
}
新增一个子类时,所有 when 处都会报错提醒你补上,这是密封类相比抽象类的安全优势。
Note
- 从 Kotlin 1.5 起,密封类和密封接口的子类可以放在同一包的不同文件里,不必挤在一个文件。但概念上还是「受限的、可知的子类集合」。
密封接口
Kotlin 1.5 起支持 sealed interface,用于限制接口的实现者:
sealed interface Result
object Success : Result
data class Failure(val error: Throwable) : Result
枚举类(enum class)
枚举是一组命名的常量,适合表示有限个互斥状态:
enum class Direction {
NORTH, SOUTH, EAST, WEST
}
enum class HttpStatus(val code: Int, val message: String) {
OK(200, "Success"),
NOT_FOUND(404, "Not Found"),
SERVER_ERROR(500, "Internal Server Error");
fun isSuccess() = code in 200..299
}
枚举特性:
- 每个枚举常量是单例。
- 可以有属性和方法。
- 默认实现
name(名字)和ordinal(序号)。 - 实现
Comparable,能比较顺序。
val status = HttpStatus.NOT_FOUND
println(status.code) // 404
println(status.name) // NOT_FOUND
println(status.isSuccess()) // false
enum vs sealed class
| 场景 | 选择 |
|---|---|
| 固定几个简单常量(星期、方向) | enum |
| 每个状态带不同数据(成功带数据、失败带错误) | sealed class |
| 子类是现有类的子类 | sealed class(enum 不能继承) |
| 需要带状态的实例 | sealed class(enum 常量不能有不同实例) |
Android 里 UI 状态大多用 sealed class,因为不同状态数据结构不同。
object 声明
前面讲过 object 做单例。这里补充几个场景。
匿名对象
需要临时实现接口或抽象类,用 object:
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
// 处理点击
}
})
Kotlin 里更常见的是直接传 Lambda(如果接口是 SAM 类型):
button.setOnClickListener { v ->
// 处理点击
}
伴生对象里用 object
工厂方法、缓存等常放伴生对象:
class NetworkClient private constructor(private val baseUrl: String) {
companion object {
private val cache = mutableMapOf<String, NetworkClient>()
fun get(baseUrl: String): NetworkClient =
cache.getOrPut(baseUrl) { NetworkClient(baseUrl) }
}
}
Android 实战:UI 状态建模
Compose 里典型的状态管理用密封类:
// 定义状态
sealed class LoginState {
object Idle : LoginState()
object Loading : LoginState()
data class Success(val userId: String) : LoginState()
data class Failure(val error: String) : LoginState()
}
// ViewModel 里
class LoginViewModel : ViewModel() {
private val _state = MutableStateFlow<LoginState>(LoginState.Idle)
val state: StateFlow<LoginState> = _state
fun login(email: String, password: String) {
viewModelScope.launch {
_state.value = LoginState.Loading
try {
val userId = repo.login(email, password)
_state.value = LoginState.Success(userId)
} catch (e: Exception) {
_state.value = LoginState.Failure(e.message ?: "登录失败")
}
}
}
}
// Compose 里渲染
@Composable
fun LoginScreen(state: LoginState) {
when (state) {
is LoginState.Idle -> LoginForm()
is LoginState.Loading -> CircularProgressIndicator()
is LoginState.Success -> Text("欢迎 ${state.userId}")
is LoginState.Failure -> Text("错误:${state.error}")
}
}
新增一种状态时,编译器会强制你在 when 里补上对应分支,避免漏处理。
小结
data class 自动生成 equals/hashCode/toString/copy,做模型首选。密封类限制子类集合,配合 when 强制完整匹配,是 Android 状态管理的核心。枚举适合有限个简单常量。object 做单例和匿名实现。下一节学集合与泛型。