Kotlin 密封类 (Sealed Class)

密封类用于限制继承关系 ,即一个类的所有子类都必须在同一个文件 中定义。它本质上是枚举的升级版------枚举用于固定的一组对象 ,密封类用于固定的一组类型

核心特点

  1. 子类受限:所有子类在编译时已知,只能出现在同一文件中;
  2. 可以有多个实例:相比枚举(每个常量只有一个实例),密封类的子类可以有多个对象;
  3. 支持状态携带:不同子类可携带不同类型的数据;

基本语法

kotlin 复制代码
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
    object Loading : Result()
}

主要使用场景

1. 状态机表示(最常见)

kotlin 复制代码
sealed class NetworkState {
    object Idle
    object Loading
    data class Success(val data: String)
    data class Error(val code: Int)
}

2. UI 事件/视图状态

kotlin 复制代码
sealed class LoginEvent {
    data class OnEmailChange(val email: String)
    data class OnPasswordChange(val pwd: String)
    object OnSubmitClick
}

与 when 表达式的完美配合

kotlin 复制代码
fun handleState(state: Result) {
    when (state) {
        is Result.Success -> println(state.data)
        is Result.Error -> println(state.message)
        Result.Loading -> println("加载中")
        // 无需 else 分支,编译器知道所有可能情况
    }
}

与其他类型的对比

特性 密封类 枚举 普通抽象类
子类型数量 固定,编译时已知 固定 无限制
单例/多实例 两者都支持 仅单例 两者都支持
携带状态 ✅ 不同子类可不同 ❌ 所有常量相同结构
模式匹配 ✅ 完整检查 ⚠️ 有限

实例:加载用户信息

kotlin 复制代码
// 1. 定义密封类表示加载状态
sealed class LoadState<out T> {
    object Idle : LoadState<Nothing>()
    object Loading : LoadState<Nothing>()
    data class Success<T>(val data: T) : LoadState<T>()
    data class Error(val message: String) : LoadState<Nothing>()
}

// 2. 模拟数据仓库
class UserRepository {
    fun fetchUser(userId: String): LoadState<User> {
        return when {
            userId.isEmpty() -> LoadState.Error("用户ID不能为空")
            userId == "404" -> LoadState.Error("用户不存在")
            else -> LoadState.Success(User(userId, "张三", 25))
        }
    }
}

data class User(val id: String, val name: String, val age: Int)

// 3. UI 层处理状态
class UserViewModel {
    private val repo = UserRepository()
    
    fun loadUser(userId: String) {
        var state: LoadState<User> = LoadState.Loading
        println("状态: 加载中...")
        
        state = repo.fetchUser(userId)
        
        // 4. 使用 when 表达式处理所有情况(无需 else)
        when (state) {
            is LoadState.Idle -> println("状态: 空闲")
            is LoadState.Loading -> println("状态: 加载中")
            is LoadState.Success -> {
                println("状态: 成功")
                println("用户: ${state.data.name}, ${state.data.age}岁")
            }
            is LoadState.Error -> println("状态: 失败 - ${state.message}")
        }
    }
}

// 5. 测试
fun main() {
    val vm = UserViewModel()
    
    println("=== 测试1: 正常加载 ===")
    vm.loadUser("123")
    
    println("\n=== 测试2: 空ID ===")
    vm.loadUser("")
    
    println("\n=== 测试3: 不存在的用户 ===")
    vm.loadUser("404")
}

注意事项

  • 密封类本身是抽象的,不能直接实例化
  • Kotlin 1.5+ 允许子类定义在不同文件的同一编译单元内,但通常还是放同一文件更清晰
  • 密封类可以继承接口,也可以被继承(仅限子类)

简单总结:当想表达 "某个值只能是几种类型之一,且每种类型可以携带不同数据" 时,就用密封类。

相关推荐
Kapaseker2 小时前
Kotlin inline:你以为它只是个性能优化?
android·kotlin
学习指针路上的小学渣2 小时前
kotlin笔记
kotlin
黄林晴3 小时前
重磅更新!Kotlin协程1.11.0 发布,Flow/StateFlow 新 API 全面升级
android·kotlin
帅次4 小时前
Navigation Compose:NavHost、NavController 与参数
android·kotlin·gradle·android jetpack·compose
UXbot1 天前
AI画原型工具如何帮非设计师快速生成UI界面
前端·vue.js·ui·kotlin·swift·原型模式·web app
赏金术士1 天前
JetPack Compose 弹窗、菜单、交互组件(五)
android·kotlin·交互·android jetpack·compose
小书房1 天前
Kotlin的协程
kotlin·高并发·协程·异步·虚拟线程·coroutinescope
小书房1 天前
Kotlin协程的运行原理
android·开发语言·kotlin·协程
赏金术士1 天前
JetPack Compose 基础核心模块(一)
android·kotlin·android jetpack·compose