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 开始,支持 expect 和 actual 机制下的密封接口,但更常见的是使用 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:

  • 密封类是受限的类层次结构,所有子类必须在同一文件中定义
  • 抽象类的继承是开放的,可以从任何地方继承
  • 密封类默认是抽象的,但不能显式声明为 abstract 或 open
  • 密封类主要用于 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 特性的深刻理解。