Skip to content

07. Kotlin 密封类(Sealed Class)

目录

  1. Sealed Class 定义和语法
  2. Sealed Class 的核心特性
  3. Sealed Class vs Enum 对比
  4. Sealed Class vs Sealed Interface
  5. when 表达式的 exhaustive 特性
  6. 状态管理中的应用
  7. 结果封装(Result/Data 类替代)
  8. 高级应用场景
  9. 最佳实践
  10. 常见错误与陷阱
  11. 性能优化
  12. 面试考点

1. Sealed Class 定义和语法

1.1 什么是密封类

密封类(Sealed Class) 是 Kotlin 1.1 版本引入的一种类层次结构限制。它是一种特殊的类,用于表示受限的类层次结构——即你能够知道一个密封类的所有可能的子类。

kotlin
// 基本定义语法
sealed class Result

data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()

1.2 关键约束

密封类有以下重要约束:

  1. 所有子类必须在同一个文件中定义(包含内部类)
  2. 密封类不能是 open 或 abstract(默认就是抽象的)
  3. 不能从外部文件继承密封类
  4. 密封类可以有构造器参数、属性和方法
kotlin
// ✅ 正确:所有子类在同一文件
sealed class Animal {
    data class Dog(val name: String, val age: Int) : Animal()
    data class Cat(val name: String, val color: String) : Animal()
    object Unknown : Animal()
}

// ❌ 错误:不能在其他文件继承
// 在其他文件中
class Bird : Animal()  // 编译错误!

// ✅ 正确:在密封类内部定义子类
sealed class Shape {
    class Circle(val radius: Double) : Shape()
    class Rectangle(val width: Double, val height: Double) : Shape()
    class Triangle(val a: Double, val b: Double, val c: Double) : Shape()
}

1.3 密封类的继承体系

密封类可以形成多层次的继承体系:

kotlin
sealed class Expression

sealed class Constant(val value: Int) : Expression()
sealed class Operator(val left: Expression, val right: Expression) : Expression()

class Number(val num: Int) : Constant(num)
class BinaryOperator(val left: Expression, val right: Expression) : Operator(left, right)
class Plus(left: Expression, right: Expression) : BinaryOperator(left, right)
class Minus(left: Expression, right: Expression) : BinaryOperator(left, right)

1.4 密封类作为构造器参数

kotlin
sealed class Permission {
    object Read : Permission()
    object Write : Permission()
    object Execute : Permission()
}

class User(val name: String, val permission: Permission) {
    fun checkAccess(): Boolean = when (permission) {
        is Permission.Read -> true
        is Permission.Write -> false
        is Permission.Execute -> true
    }
}

2. Sealed Class 的核心特性

2.1 受限的类层次结构

密封类的核心特性是编译器知道所有可能的子类,这使得:

kotlin
sealed class NetworkState

data class Loading(val progress: Int) : NetworkState()
data class Success(val data: List<String>) : NetworkState()
data class Error(val exception: Exception) : NetworkState()
object Idle : NetworkState()

fun processState(state: NetworkState) {
    // 编译器知道所有情况,不需要 when 的默认分支
    when (state) {
        is Loading -> println("Loading: ${state.progress}%")
        is Success -> println("Success: ${state.data.size} items")
        is Error -> println("Error: ${state.exception.message}")
        Idle -> println("Idle state")
    }
    // 如果添加新的子类,编译器会报错:when 表达式不是 exhaustive
}

2.2 作为 when 表达式

kotlin
sealed class UIState<T> {
    data class Initial() : UIState<Nothing>()
    data class Loading(val progress: Int) : UIState<Nothing>()
    data class Success(val data: T) : UIState<T>()
    data class Error(val message: String) : UIState<Nothing>()
}

fun <T> renderState(state: UIState<T>) {
    when (state) {
        is UIState.Initial -> showInitialScreen()
        is UIState.Loading -> showLoadingScreen(state.progress)
        is UIState.Success -> showDataScreen(state.data)
        is UIState.Error -> showErrorScreen(state.message)
    }
}

2.3 携带数据的能力

与枚举相比,密封类的每个子类都可以携带不同的数据:

kotlin
// 枚举无法携带不同类型的数据
enum class SimpleState {
    LOADING, SUCCESS, ERROR
}

// 密封类每个子类可以有自己的属性
sealed class RichState {
    data class Loading(val progress: Int, val currentStep: String) : RichState()
    data class Success<T>(val data: T, val timestamp: Long) : RichState()
    data class Error(val code: Int, val message: String, val retryCount: Int) : RichState()
}

3. Sealed Class vs Enum 对比

3.1 核心区别

特性EnumSealed Class
实例数量固定(单例)可变(可创建多个实例)
携带数据所有实例相同结构每个子类不同结构
构造函数只能一个每个子类独立
when 表达式exhaustiveexhaustive
继承不支持支持多层继承
性能更优略低(对象创建)

3.2 使用场景对比

场景 1:固定状态集合(适合 Enum)

kotlin
// 使用枚举
enum class OrderStatus {
    PENDING,
    CONFIRMED,
    SHIPPED,
    DELIVERED,
    CANCELLED
}

fun getStatusText(status: OrderStatus) = when (status) {
    OrderStatus.PENDING -> "待处理"
    OrderStatus.CONFIRMED -> "已确认"
    OrderStatus.SHIPPED -> "已发货"
    OrderStatus.DELIVERED -> "已送达"
    OrderStatus.CANCELLED -> "已取消"
}

场景 2:状态需要携带数据(适合 Sealed Class)

kotlin
// 使用密封类
sealed class OrderStatus {
    object Pending : OrderStatus()
    data class Confirmed(val orderNumber: String, val confirmTime: Long) : OrderStatus()
    data class Shipped(val trackingNumber: String, val carrier: String) : OrderStatus()
    data class Delivered(val deliveryTime: Long, val receiver: String) : OrderStatus()
    data class Cancelled(val reason: String, val refundAmount: Double) : OrderStatus()
}

fun getStatusText(status: OrderStatus): String = when (status) {
    is OrderStatus.Pending -> "待处理"
    is OrderStatus.Confirmed -> "已确认:订单号 ${status.orderNumber}"
    is OrderStatus.Shipped -> "已发货:运单号 ${status.trackingNumber}"
    is OrderStatus.Delivered -> "已送达:收件人 ${status.receiver}"
    is OrderStatus.Cancelled -> "已取消:${status.reason}"
}

3.3 何时选择 Enum vs Sealed Class

kotlin
// ✅ 使用 Enum 的场景
// 1. 状态固定且不需要携带数据
enum class WeekDay {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 2. 需要单例保证
enum class SingletonService {
    INSTANCE;
    
    fun doWork() { /* ... */ }
}

// 3. 需要按顺序遍历
enum class Priority(val level: Int) {
    LOW(1), MEDIUM(2), HIGH(3), CRITICAL(4);
    
    fun next() = values().indexOf(this).let { if (it < values().size - 1) values()[it + 1] else null }
}

// ✅ 使用 Sealed Class 的场景
// 1. 状态需要携带不同结构的数据
sealed class FormState {
    object Idle : FormState()
    data class Validating(val fieldName: String) : FormState()
    data class Valid(val form: FormData) : FormState()
    data class Invalid(val errors: Map<String, String>) : FormState()
}

// 2. 需要继承层次结构
sealed class Notification {
    sealed class System : Notification() {
        object Update : System()
        data class Security(val level: Int) : System()
    }
    
    sealed class User : Notification() {
        data class Message(val from: String, val text: String) : User()
        data class Mention(val from: String, val context: String) : User()
    }
}

// 3. 每个状态需要不同的行为
sealed class PaymentMethod {
    data class CreditCard(val number: String, val expiry: String) : PaymentMethod() {
        fun validate() = number.length == 16
    }
    
    data class PayPal(val email: String) : PaymentMethod() {
        fun validate() = email.contains("@")
    }
    
    data class Crypto(val wallet: String) : PaymentMethod() {
        fun validate() = wallet.startsWith("0x")
    }
}

4. Sealed Class vs Sealed Interface

4.1 Kotlin 1.9+ 的 Sealed Interface

从 Kotlin 1.9 开始,支持 expectactual 机制下的密封接口,但更常见的是使用 sealed interface(在 Kotlin 多平台中)。

kotlin
// Kotlin 多平台中的密封接口
sealed interface PlatformResult<T> {
    data class Success<T>(val data: T) : PlatformResult<T>
    data class Failure<T>(val error: Throwable) : PlatformResult<T>
}

4.2 Sealed Class 与 Interface 对比

kotlin
// Interface - 开放的层次结构
interface Shape {
    fun area(): Double
}

class Circle(val radius: Double) : Shape {
    override fun area() = Math.PI * radius * radius
}

class Rectangle(val width: Double, val height: Double) : Shape {
    override fun area() = width * height
}

// 外部可以继续添加实现
class Triangle(val base: Double, val height: Double) : Shape {
    override fun area() = base * height / 2
}

// Sealed Class - 封闭的层次结构
sealed class ClosedShape {
    abstract fun area(): Double
    
    class Circle(val radius: Double) : ClosedShape() {
        override fun area() = Math.PI * radius * radius
    }
    
    class Rectangle(val width: Double, val height: Double) : ClosedShape() {
        override fun area() = width * height
    }
}

// 无法在外部添加新的 ClosedShape 子类

4.3 选择建议

场景推荐
需要多模块扩展Interface
同一模块内完整覆盖Sealed Class
需要依赖注入Interface
当状态/结果类型Sealed Class
多平台项目Sealed Interface

5. when 表达式的 exhaustive 特性

5.1 编译期类型安全

密封类的核心优势是在 when 表达式中提供编译期的类型安全性:

kotlin
sealed class ValidationResult {
    object Valid : ValidationResult()
    data class Invalid(val errors: List<String>) : ValidationResult()
}

// ✅ 编译器知道所有情况,不需要 else
fun validate(result: ValidationResult) {
    when (result) {
        is ValidationResult.Valid -> {
            // 处理有效结果
        }
        is ValidationResult.Invalid -> {
            // 处理错误
        }
    }
    // 添加新子类时会编译失败,强制处理所有情况
}

5.2 省略 is 检查

当密封类的子类没有构造函数参数且是对象时,可以直接引用:

kotlin
sealed class Response<T> {
    data class Loading<T> : Response<T>()
    data class Success<T>(val data: T) : Response<T>()
    data class Failure<T>(val error: String) : Response<T>()
}

fun handleResponse(response: Response<String>) {
    when (response) {
        Response.Loading -> println("加载中...")  // 无需 is
        is Response.Success -> println("数据:${response.data}")
        is Response.Failure -> println("错误:${response.error}")
    }
}

5.3 嵌套密封类的 exhaustiveness

kotlin
sealed class Color {
    sealed class Primary : Color() {
        object Red : Primary()
        object Green : Primary()
        object Blue : Primary()
    }
    
    sealed class Secondary : Color() {
        object Orange : Secondary()
        object Purple : Secondary()
        object Brown : Secondary()
    }
    
    object Black : Color()
    object White : Color()
}

fun getColorType(color: Color) = when (color) {
    is Color.Primary -> "主色"
    is Color.Secondary -> "副色"
    Color.Black -> "黑色"
    Color.White -> "白色"
}

// 或者更细粒度
fun getColorDetail(color: Color) = when (color) {
    Color.Primary.Red -> "红色"
    Color.Primary.Green -> "绿色"
    Color.Primary.Blue -> "蓝色"
    Color.Secondary.Orange -> "橙色"
    Color.Secondary.Purple -> "紫色"
    Color.Secondary.Brown -> "棕色"
    Color.Black -> "黑色"
    Color.White -> "白色"
}

5.4 智能转换

kotlin
sealed class UserAction {
    data class Login(val username: String, val password: String) : UserAction()
    data class Logout(val sessionId: String) : UserAction()
    data class UpdateProfile(val email: String, val avatar: String) : UserAction()
    object DeleteAccount : UserAction()
}

fun processAction(action: UserAction) {
    when (action) {
        is UserAction.Login -> {
            // 这里 action.username 可以直接访问,智能转换
            println("登录用户:${action.username}")
        }
        is UserAction.Logout -> {
            println("登出会话:${action.sessionId}")
        }
        is UserAction.UpdateProfile -> {
            println("更新邮箱:${action.email}")
        }
        UserAction.DeleteAccount -> {
            println("删除账户")
        }
    }
}

6. 状态管理中的应用

6.1 UI 状态管理

kotlin
// 用户界面状态
sealed class ScreenState<T> {
    // 初始状态
    object Initial : ScreenState<Nothing>()
    
    // 加载状态
    data class Loading(val progress: Int = 0, val showIndicator: Boolean = true) : ScreenState<Nothing>()
    
    // 成功状态
    data class Success<T>(val data: T, val isLoadingMore: Boolean = false) : ScreenState<T>()
    
    // 错误状态
    data class Error<T>(
        val message: String,
        val exception: Throwable? = null,
        val canRetry: Boolean = true,
        val retryCount: Int = 0
    ) : ScreenState<Nothing>()
    
    // 空状态
    data class Empty<T>(val hint: String = "") : ScreenState<Nothing>()
    
    // 无网络状态
    object NoNetwork : ScreenState<Nothing>()
}

// 使用示例
class UserScreenViewModel : ViewModel() {
    private val _uiState = MutableLiveData<ScreenState<User>>(ScreenState.Initial)
    val uiState: LiveData<ScreenState<User>> = _uiState
    
    fun loadUser(id: String) {
        _uiState.value = ScreenState.Loading(0, true)
        
        lifecycleScope.launch {
            try {
                val user = userRepository.findById(id)
                _uiState.value = ScreenState.Success(user)
            } catch (e: NoNetworkException) {
                _uiState.value = ScreenState.NoNetwork
            } catch (e: Exception) {
                _uiState.value = ScreenState.Error(
                    message = e.message ?: "未知错误",
                    exception = e,
                    canRetry = true,
                    retryCount = 0
                )
            }
        }
    }
}

// UI 渲染
fun renderScreenState(state: ScreenState<User>) {
    when (state) {
        is ScreenState.Initial -> showNothing()
        is ScreenState.Loading -> {
            showProgressBar(state.progress)
            if (state.showIndicator) showLoadingIndicator()
        }
        is ScreenState.Success -> {
            showUserData(state.data)
            if (state.isLoadingMore) showLoadingFooter()
        }
        is ScreenState.Error -> {
            showErrorView(state.message)
            if (state.canRetry) showRetryButton()
        }
        is ScreenState.Empty -> showEmptyView(state.hint)
        ScreenState.NoNetwork -> showNoNetworkView()
    }
}

6.2 网络状态管理

kotlin
sealed class NetworkCallState<T> {
    data class NotStarted<T>() : NetworkCallState<T>()
    data class InProgress<T>(val progress: Float = 0f) : NetworkCallState<T>()
    data class Completed<T>(val result: T, val duration: Long) : NetworkCallState<T>()
    data class Failed<T>(val error: NetworkError, val attempt: Int) : NetworkCallState<T>()
    
    fun isIdle() = this is NotStarted
    fun isRunning() = this is InProgress
    fun isSuccess() = this is Completed
    fun isFailed() = this is Failed
}

sealed class NetworkError {
    data class Timeout(val timeout: Long) : NetworkError()
    data class ServerError(val code: Int, val message: String) : NetworkError()
    data class ClientError(val code: Int, val message: String) : NetworkError()
    object NoInternet : NetworkError()
    data class ParseError(val rawResponse: String) : NetworkError()
}

// 网络请求封装
suspend fun <T> safeApiCall(
    apiCall: suspend () -> T,
    timeout: Long = 30000L
): NetworkCallState<T> {
    val startTime = System.currentTimeMillis()
    
    return try {
        withTimeout(timeout) {
            val result = apiCall()
            NetworkCallState.Completed(result, System.currentTimeMillis() - startTime)
        }
    } catch (e: TimeoutCancellationException) {
        NetworkCallState.Failed(NetworkError.Timeout(timeout), 1)
    } catch (e: HttpException) {
        when (e.code()) {
            in 400..499 -> NetworkCallState.Failed(
                NetworkError.ClientError(e.code(), e.message()),
                1
            )
            in 500..599 -> NetworkCallState.Failed(
                NetworkError.ServerError(e.code(), e.message()),
                1
            )
            else -> NetworkCallState.Failed(
                NetworkError.ServerError(e.code(), "未知错误"),
                1
            )
        }
    } catch (e: NoNetworkException) {
        NetworkCallState.Failed(NetworkError.NoInternet, 1)
    } catch (e: Exception) {
        NetworkCallState.Failed(NetworkError.ParseError(e.toString()), 1)
    }
}

6.3 表单状态管理

kotlin
sealed class FormFieldState {
    object Idle : FormFieldState()
    object Validating : FormFieldState()
    object Valid : FormFieldState()
    data class Invalid(val error: String) : FormFieldState()
}

sealed class FormState {
    data class Initial(
        val emailState: FormFieldState = FormFieldState.Idle,
        val passwordState: FormFieldState = FormFieldState.Idle,
        val nameState: FormFieldState = FormFieldState.Idle
    ) : FormState() {
        fun isValid() = emailState is FormFieldState.Valid && 
                       passwordState is FormFieldState.Valid && 
                       nameState is FormFieldState.Valid
    }
    
    object Submitting : FormState()
    data class Submitted(val success: Boolean, val message: String? = null) : FormState()
}

// 表单 ViewModel
class LoginFormViewModel : ViewModel() {
    private val _formState = MutableLiveData<FormState>(FormState.Initial())
    val formState: LiveData<FormState> = _formState
    
    fun setEmail(value: String) {
        val currentState = (formState.value as? FormState.Initial) ?: return
        val newState = when {
            value.isEmpty() -> FormState.Invalid("邮箱不能为空")
            !value.contains("@") -> FormState.Invalid("邮箱格式不正确")
            else -> FormState.Valid
        }
        
        _formState.value = FormState.Initial(
            emailState = newState,
            passwordState = currentState.passwordState,
            nameState = currentState.nameState
        )
    }
    
    fun submit() {
        val currentState = _formState.value as? FormState.Initial ?: return
        if (!currentState.isValid()) return
        
        _formState.value = FormState.Submitting
        
        lifecycleScope.launch {
            try {
                repository.login(currentState.email, currentState.password)
                _formState.value = FormState.Submitted(true, "登录成功")
            } catch (e: Exception) {
                _formState.value = FormState.Submitted(false, e.message)
            }
        }
    }
}

6.4 复杂业务状态

kotlin
// 订单状态流转
sealed class OrderWorkflow {
    sealed class Draft : OrderWorkflow() {
        data class Editing(val items: List<CartItem>) : Draft()
        object Reviewing : Draft()
    }
    
    sealed class Processing : OrderWorkflow() {
        object PaymentPending : Processing()
        data class PaymentProcessing(val paymentMethod: String) : Processing()
        object InventoryChecking : Processing()
        object Packaging : Processing()
    }
    
    sealed class Shipped : OrderWorkflow() {
        data class InTransit(val trackingNumber: String, val carrier: String) : Shipped()
        object Delivered : Shipped()
    }
    
    sealed class Completed : OrderWorkflow() {
        data class Confirmed(val review: Rating? = null) : Completed()
        object Refunded : Completed()
        object Cancelled : Completed()
    }
    
    // 状态转换
    fun canTransitionTo(next: OrderWorkflow): Boolean = when {
        this is Draft && next is Processing -> true
        this is Processing && next is Shipped -> true
        this is Shipped && next is Completed -> true
        else -> false
    }
    
    // 状态进度计算
    val progress: Float
        get() = when (this) {
            is Draft -> 0.2f
            is Processing -> 0.5f
            is Shipped -> 0.8f
            is Completed -> 1.0f
        }
}

fun renderOrderStatus(status: OrderWorkflow) {
    when (status) {
        is OrderWorkflow.Draft -> {
            when (status) {
                is OrderWorkflow.Draft.Editing -> showDraftEditing(status.items)
                OrderWorkflow.Draft.Reviewing -> showDraftReviewing()
            }
        }
        is OrderWorkflow.Processing -> {
            when (status) {
                OrderWorkflow.Processing.PaymentPending -> showPaymentPending()
                is OrderWorkflow.Processing.PaymentProcessing -> 
                    showPaymentProcessing(status.paymentMethod)
                OrderWorkflow.Processing.InventoryChecking -> showInventoryChecking()
                OrderWorkflow.Processing.Packaging -> showPackaging()
            }
        }
        is OrderWorkflow.Shipped -> {
            when (status) {
                is OrderWorkflow.Shipped.InTransit -> showInTransit(status.trackingNumber, status.carrier)
                OrderWorkflow.Shipped.Delivered -> showDelivered()
            }
        }
        is OrderWorkflow.Completed -> {
            when (status) {
                is OrderWorkflow.Completed.Confirmed -> showConfirmed(status.review)
                OrderWorkflow.Completed.Refunded -> showRefunded()
                OrderWorkflow.Completed.Cancelled -> showCancelled()
            }
        }
    }
}

7. 结果封装(Result/Data 类替代)

7.1 自定义 Result 类型

kotlin
// 标准 Result 的增强版
sealed class Result<T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Failure<T>(val error: Throwable, val recoverable: Boolean = true) : Result<T>()
    data class Loading<T>(val progress: Int = 0, val currentStep: String = "") : Result<T>()
    
    // 便捷方法
    fun <R> map(transform: (T) -> R): Result<R> = when (this) {
        is Success -> Success(transform(data))
        is Failure -> Failure(error, recoverable)
        is Loading -> Loading(progress, currentStep)
    }
    
    fun <R> mapError(transform: (Throwable) -> Throwable): Result<T> = when (this) {
        is Failure -> Failure(transform(error), recoverable)
        else -> this
    }
    
    fun getOrNull(): T? = (this as? Success)?.data
    
    fun fold(
        onSuccess: (T) -> Unit,
        onFailure: (Throwable) -> Unit,
        onLoading: () -> Unit = {}
    ) {
        when (this) {
            is Success -> onSuccess(data)
            is Failure -> onFailure(error)
            is Loading -> onLoading()
        }
    }
}

// 使用示例
suspend fun fetchUser(id: String): Result<User> = try {
    val user = api.getUser(id)
    Result.Success(user)
} catch (e: Exception) {
    Result.Failure(e)
}

// 链式调用
suspend fun fetchUserDetails(userId: String): Result<UserDetails> {
    return fetchUser(userId)
        .map { user -> 
            api.getUserDetails(user.id) 
        }
        .mapError { e -> 
            UserFetchException("Failed to fetch user: ${e.message}") 
        }
}

// 组合多个 Result
suspend fun fetchUserProfile(userId: String): Result<ProfileData> {
    val userResult = fetchUser(userId)
    val detailsResult = fetchUserDetails(userId)
    val preferencesResult = fetchUserPreferences(userId)
    
    return when {
        userResult is Result.Success && 
        detailsResult is Result.Success && 
        preferencesResult is Result.Success -> {
            Result.Success(ProfileData(
                user = userResult.data,
                details = detailsResult.data,
                preferences = preferencesResult.data
            ))
        }
        else -> {
            val errors = buildList {
                (userResult as? Result.Failure)?.error?.let { add(it) }
                (detailsResult as? Result.Failure)?.error?.let { add(it) }
                (preferencesResult as? Result.Failure)?.error?.let { add(it) }
            }
            Result.Failure(MultipleErrorsException(errors))
        }
    }
}

7.2 替代传统 Success/Error 类

kotlin
// 传统方式
class UserResponse {
    var success: Boolean = false
    var data: User? = null
    var errorCode: Int = 0
    var errorMessage: String = ""
}

// 密封类方式
sealed class UserResponse {
    data class Success(val user: User, val cached: Boolean = false) : UserResponse()
    data class Error(val code: Int, val message: String, val details: String? = null) : UserResponse()
    object NotFound : UserResponse()
    object Unauthorized : UserResponse()
    
    fun isSuccess() = this is Success
    fun isError() = this is Error || this is NotFound || this is Unauthorized
    fun getErrorOrNull() = when (this) {
        is Error -> ApiError(code, message, details)
        NotFound -> ApiError(404, "用户未找到")
        Unauthorized -> ApiError(401, "未授权")
        is Success -> null
    }
}

// 解析网络响应
fun parseUserResponse(response: HttpResponse): UserResponse {
    return when (response.code) {
        200 -> {
            val user = parseUser(response.body)
            UserResponse.Success(user, response.isCached)
        }
        404 -> UserResponse.NotFound
        401 -> UserResponse.Unauthorized
        in 400..599 -> UserResponse.Error(
            response.code,
            response.body.message,
            response.body.details
        )
        else -> UserResponse.Error(500, "未知错误")
    }
}

// UI 处理
fun handleUserResponse(response: UserResponse) {
    when (response) {
        is UserResponse.Success -> {
            showUser(response.user)
            if (response.cached) showToast("显示缓存数据")
        }
        is UserResponse.Error -> {
            showError(response.message)
            if (response.code == 401) navigateToLogin()
        }
        UserResponse.NotFound -> {
            showNotFound()
        }
        UserResponse.Unauthorized -> {
            showUnauthorized()
        }
    }
}

7.3 多层嵌套结果

kotlin
// 复杂业务场景的结果封装
sealed class PaymentResult {
    // 成功
    sealed class Success : PaymentResult() {
        data class Approved(val transactionId: String, val amount: BigDecimal) : Success()
        data class Pending(val referenceId: String, val expiresAt: Instant) : Success()
        data class Partial(
            val approvedAmount: BigDecimal, 
            val declinedAmount: BigDecimal,
            val transactionId: String
        ) : Success()
    }
    
    // 失败
    sealed class Failure : PaymentResult() {
        sealed class Rejected : Failure() {
            object InsufficientFunds : Rejected()
            object CardExpired : Rejected()
            object InvalidCardNumber : Rejected()
            object FraudDetected : Rejected()
            object TooManyAttempts : Rejected()
            data class BankError(val code: String, val message: String) : Rejected()
        }
        
        sealed class Technical : Failure() {
            object Timeout : Technical()
            object GatewayUnavailable : Technical()
            object NetworkError : Technical()
            data class SystemError(val code: String, val recoverable: Boolean) : Technical()
        }
    }
    
    // 重试建议
    fun shouldRetry(): Boolean = when (this) {
        is Failure.Technical.Timeout -> true
        is Failure.Technical.GatewayUnavailable -> true
        is Failure.Technical.NetworkError -> true
        is Failure.Technical.SystemError -> recoverable
        else -> false
    }
    
    // 用户提示
    fun userMessage(): String = when (this) {
        is Success.Approved -> "支付成功:¥$amount"
        is Success.Pending -> "支付处理中"
        is Success.Partial -> "部分支付成功"
        is Failure.Rejected.InsufficientFunds -> "余额不足"
        is Failure.Rejected.CardExpired -> "卡片已过期"
        is Failure.Rejected.InvalidCardNumber -> "卡号无效"
        is Failure.Rejected.FraudDetected -> "支付被风控拦截"
        is Failure.Rejected.TooManyAttempts -> "尝试次数过多"
        is Failure.Technical.Timeout -> "请求超时"
        is Failure.Technical.GatewayUnavailable -> "支付网关不可用"
        is Failure.Technical.NetworkError -> "网络错误"
        is Failure.Technical.SystemError -> "系统错误"
    }
}

8. 高级应用场景

8.1 命令模式实现

kotlin
sealed class Command {
    // 导航命令
    sealed class Navigate : Command() {
        data class ToScreen(val screen: String, val args: Bundle = Bundle()) : Navigate()
        object Back : Navigate()
        object PopToRoot : Navigate()
    }
    
    // 数据操作命令
    sealed class DataOperation : Command() {
        data class FetchData(val endpoint: String, val cachePolicy: CachePolicy) : DataOperation()
        data class UpdateData(val entityId: String, val updates: Map<String, Any>) : DataOperation()
        data class DeleteData(val entityId: String, val confirm: Boolean = false) : DataOperation()
    }
    
    // UI 反馈命令
    sealed class UIFeedback : Command() {
        data class ShowToast(val message: String, val duration: Int = Toast.LENGTH_SHORT) : UIFeedback()
        data class ShowSnackbar(val message: String, val action: String? = null) : UIFeedback()
        data class ShowDialog(val title: String, val message: String, val actions: List<DialogAction>) : UIFeedback()
        object ShowProgress : UIFeedback()
        object HideProgress : UIFeedback()
    }
    
    // 侧边效应命令
    sealed class SideEffect : Command() {
        data class TrackEvent(val eventName: String, val properties: Map<String, Any> = emptyMap()) : SideEffect()
        data class Analytics(val type: AnalyticsType, val data: Any) : SideEffect()
        data class CacheData(val key: String, val value: Any, val expiry: Long) : SideEffect()
    }
}

// 命令处理器
class CommandDispatcher(
    private val navigator: Navigator,
    private val repository: Repository,
    private val analytics: AnalyticsManager
) {
    fun dispatch(command: Command) {
        when (command) {
            is Command.Navigate.ToScreen -> navigator.navigate(command.screen, command.args)
            is Command.Navigate.Back -> navigator.popBackStack()
            is Command.Navigate.PopToRoot -> navigator.popBackStackToRoot()
            
            is Command.DataOperation.FetchData -> {
                lifecycleScope.launch {
                    repository.fetch(command.endpoint, command.cachePolicy)
                }
            }
            is Command.DataOperation.UpdateData -> {
                repository.update(command.entityId, command.updates)
            }
            is Command.DataOperation.DeleteData -> {
                if (command.confirm) {
                    repository.delete(command.entityId)
                }
            }
            
            is Command.UIFeedback.ShowToast -> {
                Toast.makeText(context, command.message, command.duration).show()
            }
            is Command.UIFeedback.ShowDialog -> {
                showDialog(command.title, command.message, command.actions)
            }
            
            is Command.SideEffect.TrackEvent -> {
                analytics.track(command.eventName, command.properties)
            }
            is Command.SideEffect.CacheData -> {
                cache.save(command.key, command.value, command.expiry)
            }
        }
    }
}

8.2 状态机实现

kotlin
sealed class State {
    abstract val id: String
    abstract fun handle(event: Event): Transition
    
    data class Idle(val id: String = "idle") : State() {
        override fun handle(event: Event): Transition = when (event) {
            is Event.Start -> Transition(Loading, "用户点击开始")
            else -> Transition(this, "无效事件")
        }
    }
    
    data class Loading(val id: String = "loading") : State() {
        override fun handle(event: Event): Transition = when (event) {
            is Event.DataLoaded -> Transition(Success, "数据加载完成")
            is Event.Error -> Transition(Error, "发生错误:${event.message}")
            is Event.Cancel -> Transition(Idle, "用户取消")
            else -> Transition(this, "无效事件")
        }
    }
    
    data class Success(val data: Any, val id: String = "success") : State() {
        override fun handle(event: Event): Transition = when (event) {
            is Event.Reset -> Transition(Idle, "重置到初始状态")
            is Event.Refresh -> Transition(Loading, "刷新数据")
            else -> Transition(this, "无效事件")
        }
    }
    
    data class Error(val message: String, val id: String = "error") : State() {
        override fun handle(event: Event): Transition = when (event) {
            is Event.Retry -> Transition(Loading, "重试")
            is Event.Ignore -> Transition(Idle, "忽略错误")
            else -> Transition(this, "无效事件")
        }
    }
}

sealed class Event {
    object Start : Event()
    object DataLoaded : Event()
    data class Error(val message: String) : Event()
    object Cancel : Event()
    object Reset : Event()
    object Refresh : Event()
    object Retry : Event()
    object Ignore : Event()
}

data class Transition(val nextState: State, val reason: String)

class StateMachine(private val initialState: State = State.Idle()) {
    private var currentState: State = initialState
    
    fun handle(event: Event): State {
        val transition = currentState.handle(event)
        println("状态转换:${currentState.id} -> ${transition.nextState.id} (原因:${transition.reason})")
        currentState = transition.nextState
        return currentState
    }
    
    fun getState(): State = currentState
}

8.3 领域驱动设计 (DDD) 中的应用

kotlin
// 聚合根
sealed class OrderStatus {
    sealed class Draft : OrderStatus() {
        object Created : Draft()
        data class ItemAdded(val productId: String, val quantity: Int) : Draft()
        data class ItemRemoved(val productId: String) : Draft()
    }
    
    sealed class Confirmed : OrderStatus() {
        object PaymentPending : Confirmed()
        object PaymentSuccess : Confirmed()
    }
    
    sealed class Processing : OrderStatus() {
        object InventoryAllocated : Processing()
        object Packed : Processing()
    }
    
    sealed class Shipped : OrderStatus() {
        data class CarrierPicked(val carrier: String, val trackingNumber: String) : Shipped()
        object InTransit : Shipped()
        object OutForDelivery : Shipped()
    }
    
    sealed class Completed : OrderStatus() {
        object Delivered : Completed()
        object Returned : Completed()
        object Refunded : Completed()
    }
    
    // 状态验证规则
    abstract fun canTransitionTo(next: OrderStatus): Boolean
    abstract fun transitionRules(): List<String>
}

// 使用状态机的订单
class Order(
    val id: String,
    private var status: OrderStatus = OrderStatus.Draft.Created
) {
    fun apply(event: OrderEvent): Result<String> {
        return when (event) {
            is OrderEvent.ItemAdded -> {
                when (status) {
                    is OrderStatus.Draft -> {
                        status = OrderStatus.Draft.ItemAdded(event.productId, event.quantity)
                        Result.Success("商品已添加")
                    }
                    else -> Result.Error("订单已确认,不能添加商品")
                }
            }
            is OrderEvent.PaymentCompleted -> {
                when (status) {
                    is OrderStatus.Draft, is OrderStatus.Confirmed -> {
                        status = OrderStatus.Confirmed.PaymentSuccess
                        Result.Success("支付成功")
                    }
                    else -> Result.Error("订单状态不允许支付")
                }
            }
            // ... 更多事件处理
        }
    }
    
    fun getStatus(): OrderStatus = status
}

9. 最佳实践

9.1 命名规范

kotlin
// ✅ 好的命名
sealed class AuthenticationState {
    object Unauthenticated : AuthenticationState()
    object Authenticating : AuthenticationState()
    data class Authenticated(val token: String, val expiry: Date) : AuthenticationState()
    data class AuthFailed(val reason: AuthenticationError) : AuthenticationState()
}

// ❌ 不好的命名
sealed class Auth {  // 太简单,含义不明确
    object UNAUTH : Auth()  // 大小写不规范
    object AUTHENTICATING : Auth()
    data class AUTH(val TOKEN: String) : Auth()  // 属性命名混乱
}

// ✅ 推荐的命名模式
sealed class Resource<out T> {  // 使用泛型上界
    data class Idle<out T>() : Resource<T>()
    data class Loading<out T>(val progress: Int) : Resource<T>()
    data class Success<out T>(val data: T) : Resource<T>()
    data class Failure<out T>(val exception: Throwable) : Resource<T>()
}

9.2 使用 object vs data class

kotlin
// 没有数据的子类使用 object
sealed class MenuState {
    object Closed : MenuState()  // ✅ 无参数,使用 object
    object Opening : MenuState()
    object Open : MenuState()
    object Closing : MenuState()
}

// 有数据的子类使用 data class
sealed class SearchResult {
    data class Loading(val query: String) : SearchResult()  // ✅ 有参数
    data class Success(val items: List<Item>) : SearchResult()
    data class Error(val message: String) : SearchResult()
    data class NoResults(val query: String) : SearchResult()
}

// 混合使用
sealed class ViewState {
    object Initial : ViewState()
    data class Loading(val items: List<Item> = emptyList()) : ViewState()  // 有默认值
    data class Success(val items: List<Item>, val hasNext: Boolean) : ViewState()
    data class Error(val error: String, val canRetry: Boolean = true) : ViewState()
}

9.3 避免过度嵌套

kotlin
// ❌ 过度嵌套
sealed class UserState {
    sealed class AuthState : UserState() {
        sealed class LoginState : AuthState() {
            object Idle : LoginState()
            object LoggingIn : LoginState()
            data class Success(val user: User) : LoginState()
            data class Error(val error: String) : LoginState()
        }
        
        sealed class RegisterState : AuthState() {
            object Idle : RegisterState()
            data class FillingForm(val form: RegisterForm) : RegisterState()
            data class Verifying(val email: String) : RegisterState()
            data class Success(val user: User) : RegisterState()
            data class Error(val error: String) : RegisterState()
        }
    }
}

// ✅ 扁平化
sealed class UserState {
    object AuthIdle : UserState()
    data class LoggingIn(val progress: Int) : UserState()
    data class LoginSuccess(val user: User) : UserState()
    data class LoginError(val error: String) : UserState()
    
    object RegisterIdle : UserState()
    data class RegisteringForm(val form: RegisterForm) : UserState()
    data class RegisterVerifying(val email: String) : UserState()
    data class RegisterSuccess(val user: User) : UserState()
    data class RegisterError(val error: String) : UserState()
}

9.4 提供便捷方法

kotlin
sealed class NetworkResult<out T> {
    data class Success<out T>(val data: T) : NetworkResult<T>()
    data class Error(val exception: Throwable, val code: Int? = null) : NetworkResult<Nothing>()
    object Loading : NetworkResult<Nothing>()
    
    // 便捷转换
    fun <R> map(transform: (T) -> R): NetworkResult<R> = when (this) {
        is Success -> Success(transform(data))
        is Error -> Error(exception, code)
        Loading -> Loading
    }
    
    fun <R> flatMap(transform: (T) -> NetworkResult<R>): NetworkResult<R> = when (this) {
        is Success -> transform(data)
        else -> this as NetworkResult<R>
    }
    
    // 便捷访问
    fun getOrNull(): T? = (this as? Success)?.data
    fun getOrThrow(): T = when (this) {
        is Success -> data
        is Error -> throw exception
        Loading -> throw IllegalStateException("Data still loading")
    }
    
    // 便捷组合
    fun fold(
        onSuccess: (T) -> Unit,
        onError: (Throwable, Int?) -> Unit,
        onLoading: () -> Unit = {}
    ) {
        when (this) {
            is Success -> onSuccess(data)
            is Error -> onError(exception, code)
            Loading -> onLoading()
        }
    }
}

10. 常见错误与陷阱

10.1 忘记处理所有情况

kotlin
sealed class PaymentMethod {
    object CreditCard : PaymentMethod()
    object PayPal : PaymentMethod()
    object Crypto : PaymentMethod()
}

// ❌ 错误:编译器会报错,但有时会被忽略
fun processPayment(method: PaymentMethod) {
    when (method) {
        is PaymentMethod.CreditCard -> processCard()
        is PaymentMethod.PayPal -> processPayPal()
        // 忘记处理 Crypto
    }
}

// ✅ 正确:确保处理所有情况
fun processPayment(method: PaymentMethod) {
    when (method) {
        is PaymentMethod.CreditCard -> processCard()
        is PaymentMethod.PayPal -> processPayPal()
        is PaymentMethod.Crypto -> processCrypto()
    }
}

10.2 在错误位置定义子类

kotlin
// ❌ 错误:子类在不同文件
// File: Base.kt
sealed class Animal {
    data class Dog(val name: String) : Animal()
}

// File: Cat.kt
data class Cat(val name: String) : Animal()  // 编译错误!

// ✅ 正确:在同一文件
// File: Animals.kt
sealed class Animal {
    data class Dog(val name: String) : Animal()
    data class Cat(val name: String) : Animal()
}

10.3 过度使用密封类

kotlin
// ❌ 错误:简单的标志位不需要密封类
sealed class BooleanState {
    object True : BooleanState()
    object False : BooleanState()
}

// ✅ 正确:使用 Boolean
val isComplete = true

// ❌ 错误:可以用 Enum 的场景
sealed class WeekDayState {
    object Monday : WeekDayState()
    object Tuesday : WeekDayState()
    object Wednesday : WeekDayState()
    object Thursday : WeekDayState()
    object Friday : WeekDayState()
    object Saturday : WeekDayState()
    object Sunday : WeekDayState()
}

// ✅ 正确:使用 Enum
enum class WeekDay {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

10.4 性能问题

kotlin
// ❌ 错误:频繁创建临时对象
sealed class Calculation {
    data class Addition(val a: Int, val b: Int) : Calculation()
    data class Subtraction(val a: Int, val b: Int) : Calculation()
}

fun processCalculations(calculations: List<Calculation>) {
    for (calc in calculations) {
        val temp = when (calc) {
            is Calculation.Addition -> calc.a + calc.b
            is Calculation.Subtraction -> calc.a - calc.b
        }
        // 每次循环都创建新的临时对象
    }
}

// ✅ 正确:减少不必要的对象创建
fun processCalculations(calculations: List<Calculation>) {
    val results = calculations.map { calc ->
        when (calc) {
            is Calculation.Addition -> calc.a + calc.b
            is Calculation.Subtraction -> calc.a - calc.b
        }
    }
}

11. 性能优化

11.1 对象复用

kotlin
// 对于无状态的子类,使用 object 实现单例
sealed class Operator {
    object Plus : Operator()
    object Minus : Operator()
    object Multiply : Operator()
    object Divide : Operator()
    
    // 这些 object 是单例,不会重复创建
}

// 性能测试
fun benchmark() {
    val start = System.nanoTime()
    repeat(1000000) {
        val op = Operator.Plus  // 直接引用,无对象创建开销
    }
    val end = System.nanoTime()
    println("Time: ${(end - start) / 1_000_000} ms")
}

11.2 减少 when 分支

kotlin
// ❌ 低效:过多的分支
sealed class Color {
    object Red : Color()
    object Green : Color()
    object Blue : Color()
    object Yellow : Color()
    object Purple : Color()
    object Orange : Color()
    object Brown : Color()
    object Black : Color()
    object White : Color()
    object Gray : Color()
    // ... 更多颜色
}

fun getColorCategory(color: Color): String {
    return when (color) {
        Color.Red, Color.Yellow, Color.Orange -> "暖色"
        Color.Blue, Color.Green, Color.Purple -> "冷色"
        Color.White, Color.Black, Color.Gray -> "中性色"
        Color.Brown -> "大地色"
    }
}

// ✅ 高效:使用密封类分组
sealed class Color {
    sealed class Warm : Color() {
        object Red : Warm()
        object Yellow : Warm()
        object Orange : Warm()
    }
    
    sealed class Cool : Color() {
        object Blue : Cool()
        object Green : Cool()
        object Purple : Cool()
    }
    
    sealed class Neutral : Color() {
        object White : Neutral()
        object Black : Neutral()
        object Gray : Neutral()
    }
    
    object Brown : Color()
}

fun getColorCategory(color: Color): String = when (color) {
    is Color.Warm -> "暖色"
    is Color.Cool -> "冷色"
    is Color.Neutral -> "中性色"
    Color.Brown -> "大地色"
}

11.3 避免不必要的装箱

kotlin
// ❌ 低效:使用对象包装基本类型
sealed class NumberResult {
    data class IntValue(val value: Int) : NumberResult()
    data class LongValue(val value: Long) : NumberResult()
    data class DoubleValue(val value: Double) : NumberResult()
}

// ✅ 高效:使用泛型
sealed class NumberResult<T : Number> {
    abstract val value: T
    
    class IntValue(override val value: Int) : NumberResult<Int>()
    class LongValue(override val value: Long) : NumberResult<Long>()
    class DoubleValue(override val value: Double) : NumberResult<Double>()
}

12. 面试考点

12.1 基础考点

Q1: 什么是密封类?它与抽象类有什么区别?

A:

  • 密封类是受限的类层次结构,所有子类必须在同一文件中定义
  • 抽象类的继承是开放的,可以从任何地方继承
  • 密封类默认是抽象的,但不能显式声明为 abstractopen
  • 密封类主要用于 when 表达式的 exhaustive 检查

Q2: 密封类的所有子类必须在同一文件中,包括内部类吗?

A: 是的,密封类的子类必须在同一文件中,但可以在:

  • 同一文件的顶层
  • 同一文件的对象或类内部(作为内部类)
kotlin
sealed class Shape {
    // 内部类
    class Circle(val radius: Double) : Shape()
    
    // 嵌套在对象中
    object Square {
        class Instance(val size: Double) : Shape()
    }
}

// 顶层类
data class Triangle(val a: Double, val b: Double, val c: Double) : Shape()

Q3: 密封类可以有构造器参数吗?

A: 可以,密封类本身和它的所有子类都可以有构造器参数:

kotlin
sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val message: String, val code: Int) : Result<Nothing>()
}

12.2 进阶考点

Q4: 密封类和枚举的区别是什么?如何选择?

A:

特性枚举密封类
实例固定单例可创建多个实例
数据所有实例相同每个子类不同
继承不支持支持多层
when exhaustive
性能更优略低

选择:

  • 固定状态集、不需要数据 → Enum
  • 状态需要携带数据 → Sealed Class
  • 需要继承层次 → Sealed Class

Q5: 如何在使用密封类时优化性能?

A:

  1. 使用 object 代替 data class(无参数时)
  2. 减少 when 分支数量(通过嵌套分组)
  3. 避免在循环中频繁创建对象
  4. 使用泛型上界减少类型转换
kotlin
// 性能优化示例
sealed class Operator {
    // object 是单例
    object Plus : Operator()
    object Minus : Operator()
    
    // 减少分支
    sealed class Arithmetic : Operator() {
        object Add : Arithmetic()
        object Subtract : Arithmetic()
    }
    
    sealed class Comparison : Operator() {
        object GreaterThan : Comparison()
        object LessThan : Comparison()
    }
}

Q6: 密封类在多模块项目中有什么限制?

A:

  • 限制: 所有子类必须在同一文件中
  • 解决方案:
    1. 将密封类和子类放在公共模块
    2. 使用接口代替密封类(牺牲 exhaustive 检查)
    3. 使用 sealed interface(Kotlin 1.9+ 多平台)

12.3 高级考点

Q7: 实现一个完整的状态管理系统,使用密封类

A:

kotlin
sealed class ViewState<T> {
    data class Idle<T>() : ViewState<T>()
    data class Loading<T>(val progress: Int = 0, val currentStep: String = "") : ViewState<T>()
    data class Success<T>(val data: T, val hasMore: Boolean = false) : ViewState<T>()
    data class Error<T>(val error: String, val canRetry: Boolean = true) : ViewState<T>()
    data class Empty<T>(val hint: String = "") : ViewState<T>()
    
    // 组合操作
    fun <R> map(transform: (T) -> R): ViewState<R> = when (this) {
        is Success -> ViewState.Success(transform(data), hasMore)
        is Loading -> ViewState.Loading(progress, currentStep) as ViewState<R>
        is Error -> ViewState.Error(error, canRetry) as ViewState<R>
        is Empty -> ViewState.Empty(hint) as ViewState<R>
        is Idle -> ViewState.Idle() as ViewState<R>
    }
    
    // 状态转换
    fun isLoading(): Boolean = this is Loading
    fun isSuccess(): Boolean = this is Success
    fun isError(): Boolean = this is Error
}

// ViewModel 中的使用
class DataViewModel : ViewModel() {
    private val _state = MutableStateFlow<ViewState<List<Item>>>(ViewState.Idle())
    val state: StateFlow<ViewState<List<Item>>> = _state.asStateFlow()
    
    fun loadItems(page: Int = 0) {
        _state.update { ViewState.Loading(0, "加载中...") }
        
        viewModelScope.launch {
            try {
                val items = repository.getItems(page)
                _state.value = ViewState.Success(items)
            } catch (e: Exception) {
                _state.value = ViewState.Error(e.message ?: "加载失败")
            }
        }
    }
}

// UI 处理
fun ViewBinding.bind(state: ViewState<List<Item>>) {
    when (state) {
        is ViewState.Idle -> showInitial()
        is ViewState.Loading -> showLoading(state.progress, state.currentStep)
        is ViewState.Success -> {
            showItems(state.data)
            if (state.hasMore) showLoadMore()
        }
        is ViewState.Error -> {
            showError(state.error)
            if (state.canRetry) showRetryButton()
        }
        is ViewState.Empty -> showEmpty(state.hint)
    }
}

Q8: 设计一个支持撤销/重做的命令模式

A:

kotlin
sealed class Command {
    data class AddItem(val item: Item) : Command()
    data class RemoveItem(val itemId: String) : Command()
    data class UpdateItem(val itemId: String, val updates: Map<String, Any>) : Command()
    
    // 撤销操作
    abstract fun undo(): Command
    
    override fun undo(): Command = when (this) {
        is AddItem -> RemoveItem(item.id)
        is RemoveItem -> AddItem(Item(id = itemId))
        is UpdateItem -> UpdateItem(itemId, emptyMap())
    }
}

class CommandHistory {
    private val undoStack = Stack<Command>()
    private val redoStack = Stack<Command>()
    
    fun execute(command: Command) {
        // 执行命令
        // ...
        undoStack.push(command)
        redoStack.clear()
    }
    
    fun undo(): Command? {
        if (undoStack.isEmpty()) return null
        val command = undoStack.pop()
        command.undo().let { 
            // 执行撤销命令
            redoStack.push(it)
        }
        return command
    }
    
    fun redo(): Command? {
        if (redoStack.isEmpty()) return null
        val command = redoStack.pop()
        // 执行重做命令
        undoStack.push(command)
        return command
    }
}

Q9: 如何在协程中使用密封类处理异步状态?

A:

kotlin
sealed class AsyncResult<out T> {
    data class Pending<out T>() : AsyncResult<T>()
    data class Success<out T>(val data: T) : AsyncResult<T>()
    data class Failure<out T>(val exception: Throwable) : AsyncResult<T>()
    
    // 转换为 Flow
    fun asFlow(): Flow<AsyncResult<T>> = flow {
        emit(this@asFlow)
    }
    
    // 转换为 StateFlow
    fun asStateFlow(): StateFlow<AsyncResult<T>> = MutableStateFlow(this@asStateFlow)
}

// 使用示例
class DataRepository {
    suspend fun fetchData(): AsyncResult<Data> {
        return try {
            AsyncResult.Success(api.getData())
        } catch (e: Exception) {
            AsyncResult.Failure(e)
        }
    }
}

class DataViewModel : ViewModel() {
    private val _result = MutableStateFlow<AsyncResult<Data>>(AsyncResult.Pending())
    val result: StateFlow<AsyncResult<Data>> = _result
    
    init {
        viewModelScope.launch {
            _result.value = AsyncResult.Pending()
            val result = repository.fetchData()
            _result.value = result
        }
    }
}

// UI 观察
lifecycleScope.launch {
    viewModel.result.collect { result ->
        when (result) {
            is AsyncResult.Pending -> showLoading()
            is AsyncResult.Success -> showData(result.data)
            is AsyncResult.Failure -> showError(result.exception)
        }
    }
}

Q10: 密封类在响应式编程中的应用

A:

kotlin
sealed class DataChange {
    data class Inserted<T>(val item: T) : DataChange<T>()
    data class Updated<T>(val oldItem: T, val newItem: T) : DataChange<T>()
    data class Removed<T>(val item: T) : DataChange<T>()
    object Cleared : DataChange<Nothing>()
}

class DataStore<T> {
    private val _changes = MutableSharedFlow<DataChange<T>>()
    val changes: SharedFlow<DataChange<T>> = _changes
    
    suspend fun insert(item: T) {
        _changes.emit(DataChange.Inserted(item))
    }
    
    suspend fun update(oldItem: T, newItem: T) {
        _changes.emit(DataChange.Updated(oldItem, newItem))
    }
    
    suspend fun remove(item: T) {
        _changes.emit(DataChange.Removed(item))
    }
}

// UI 处理
lifecycleScope.launch {
    dataStore.changes.collect { change ->
        when (change) {
            is DataChange.Inserted -> adapter.insert(change.item)
            is DataChange.Updated -> adapter.update(change.oldItem, change.newItem)
            is DataChange.Removed -> adapter.remove(change.item)
            DataChange.Cleared -> adapter.clear()
        }
    }
}

12.4 实战题目

题目 1: 设计一个完整的用户认证状态系统

要求:

  • 包含未登录、登录中、已登录、登录失败等状态
  • 支持 Token 刷新
  • 支持 Session 过期处理
kotlin
sealed class AuthenticationState {
    // 未认证
    object Unauthenticated : AuthenticationState()
    
    // 认证中
    sealed class Authenticating : AuthenticationState() {
        object SigningIn : Authenticating()
        object Registering : Authenticating()
        data class RefreshingToken(val lastToken: String) : Authenticating()
    }
    
    // 已认证
    data class Authenticated(
        val token: String,
        val refreshToken: String,
        val expiresAt: Instant,
        val user: UserInfo
    ) : AuthenticationState()
    
    // 认证失败
    sealed class AuthenticationFailed : AuthenticationState() {
        data class InvalidCredentials(val reason: String) : AuthenticationFailed()
        object SessionExpired : AuthenticationFailed()
        data class ServerError(val code: Int, val message: String) : AuthenticationFailed()
        object NetworkError : AuthenticationFailed()
    }
    
    // Token 即将过期
    data class TokenExpiring(val remainingSeconds: Long) : AuthenticationState()
}

// 状态管理
class AuthenticationManager {
    private val _state = MutableStateFlow<AuthenticationState>(AuthenticationState.Unauthenticated)
    val state: StateFlow<AuthenticationState> = _state
    
    suspend fun login(username: String, password: String): Result<Unit> {
        _state.value = AuthenticationState.Authenticating.SigningIn
        
        return try {
            val tokens = api.login(username, password)
            val user = api.getCurrentUser()
            _state.value = AuthenticationState.Authenticated(
                token = tokens.accessToken,
                refreshToken = tokens.refreshToken,
                expiresAt = Instant.now().plusSeconds(3600),
                user = user
            )
            Result.success(Unit)
        } catch (e: AuthException) {
            _state.value = AuthenticationState.AuthenticationFailed.InvalidCredentials(e.message)
            Result.failure(e)
        } catch (e: NetworkException) {
            _state.value = AuthenticationState.AuthenticationFailed.NetworkError
            Result.failure(e)
        }
    }
    
    suspend fun refreshToken(): Result<Unit> {
        if (_state.value !is AuthenticationState.Authenticated) return Result.failure(IllegalStateException())
        
        val current = _state.value as AuthenticationState.Authenticated
        _state.value = AuthenticationState.Authenticating.RefreshingToken(current.token)
        
        return try {
            val tokens = api.refreshToken(current.refreshToken)
            _state.value = AuthenticationState.Authenticated(
                token = tokens.accessToken,
                refreshToken = tokens.refreshToken,
                expiresAt = Instant.now().plusSeconds(3600),
                user = current.user
            )
            Result.success(Unit)
        } catch (e: Exception) {
            _state.value = AuthenticationState.AuthenticationFailed.SessionExpired
            Result.failure(e)
        }
    }
    
    fun logout() {
        _state.value = AuthenticationState.Unauthenticated
    }
    
    // 监听 Token 过期
    init {
        viewModelScope.launch {
            while (true) {
                delay(60_000) // 每分钟检查
                val state = _state.value
                if (state is AuthenticationState.Authenticated) {
                    val remaining = state.expiresAt minus Instant.now()
                    if (remaining.toMillis() < 5 * 60 * 1000) { // 5 分钟内过期
                        _state.value = AuthenticationState.TokenExpiring(remaining.toSeconds())
                        refreshToken()
                    }
                }
            }
        }
    }
}

题目 2: 实现一个支持错误恢复的网络请求状态

要求:

  • 支持重试机制
  • 区分可恢复和不可恢复错误
  • 记录重试历史
kotlin
sealed class NetworkRequestState<T> {
    data class Idle<T>() : NetworkRequestState<T>()
    data class InProgress<T>(
        val progress: Float = 0f,
        val currentStep: String = "",
        val attempt: Int = 1
    ) : NetworkRequestState<T>()
    
    data class Success<T>(
        val data: T,
        val cached: Boolean = false,
        val timestamp: Long = System.currentTimeMillis()
    ) : NetworkRequestState<T>()
    
    sealed class Error<T> : NetworkRequestState<T>() {
        // 可恢复错误
        sealed class Retryable : Error<Nothing>() {
            object Timeout : Retryable()
            object NetworkUnavailable : Retryable()
            object ServerUnavailable : Retryable()
        }
        
        // 不可恢复错误
        sealed class NonRetryable : Error<Nothing>() {
            data class NotFound(val resourceId: String) : NonRetryable()
            data class Unauthorized(val reason: String) : NonRetryable()
            data class InvalidRequest(val field: String, val message: String) : NonRetryable()
            object Forbidden : NonRetryable()
        }
        
        data class RetryExhausted<T>(
            val lastError: Error<T>,
            val maxRetries: Int,
            val retryHistory: List<RetryAttempt>
        ) : Error<T>()
    }
}

data class RetryAttempt(
    val attempt: Int,
    val timestamp: Long,
    val error: String,
    val delay: Long
)

class NetworkRequestManager {
    private val _state = MutableStateFlow<NetworkRequestState<Data>>(NetworkRequestState.Idle())
    val state: StateFlow<NetworkRequestState<Data>> = _state
    
    private val maxRetries = 3
    private val retryDelays = listOf(1000L, 2000L, 4000L) // 指数退避
    
    suspend fun fetchData(endpoint: String): NetworkRequestState<Data> {
        _state.value = NetworkRequestState.InProgress(0f, "初始化", 1)
        
        var retryHistory = emptyList<RetryAttempt>()
        
        for (attempt in 1..maxRetries) {
            _state.value = NetworkRequestState.InProgress(
                progress = (attempt * 100 / maxRetries).toFloat(),
                currentStep = "第 $attempt 次尝试",
                attempt = attempt
            )
            
            try {
                val response = api.fetch(endpoint)
                return NetworkRequestState.Success(response.data)
            } catch (e: RetryableException) {
                retryHistory = retryHistory + RetryAttempt(
                    attempt = attempt,
                    timestamp = System.currentTimeMillis(),
                    error = e.message,
                    delay = retryDelays[attempt - 1]
                )
                
                if (attempt < maxRetries) {
                    delay(retryDelays[attempt - 1])
                    continue
                }
            }
        }
        
        return NetworkRequestState.Error.RetryExhausted(
            lastError = NetworkRequestState.Error.Retryable.Timeout,
            maxRetries = maxRetries,
            retryHistory = retryHistory
        )
    }
}

总结

密封类是 Kotlin 中一个强大的特性,它通过限制类层次结构,提供了编译期的类型安全性。在 Android 开发中,密封类广泛应用于:

  1. UI 状态管理 - 替代传统的 enum 和多个 boolean 标志
  2. 网络请求封装 - 优雅地处理 Success/Error/Loading 状态
  3. 结果封装 - 替代传统的 Result 类和异常处理
  4. 命令模式 - 实现可组合、可撤销的命令
  5. 状态机 - 建模复杂的业务状态流转

掌握密封类的使用,不仅能写出更安全的代码,还能在面试中展现对 Kotlin 特性的深刻理解。