首页 / Android 入门教程 / 数据类与密封类

Android 入门教程

数据类与密封类

本教程共 100 篇 · 第 14 篇 · 更新于 2026-07-28 · 约 7 分钟阅读

AndroidAndroid 入门教程Kotlin数据类密封类枚举类object状态管理

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 参数。
  • 不能是 abstractopensealedinner
  • 数据类非常适合做模型层(网络响应、数据库实体)。
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 做单例和匿名实现。下一节学集合与泛型。