Kotlin 密封类 #
一、密封类概述 #
密封类(sealed class)用于表示受限的类层次结构,所有子类必须在与密封类相同的文件中定义。
二、定义密封类 #
2.1 基本语法 #
kotlin
sealed class Result
class Success(val data: String) : Result()
class Error(val message: String) : Result()
object Loading : Result()
2.2 使用场景 #
kotlin
fun handle(result: Result): String {
return when (result) {
is Success -> "Success: ${result.data}"
is Error -> "Error: ${result.message}"
Loading -> "Loading..."
}
}
2.3 完整示例 #
kotlin
sealed class UIState {
object Loading : UIState()
data class Success(val data: String) : UIState()
data class Error(val message: String) : UIState()
}
class ViewModel {
private var state: UIState = UIState.Loading
fun setState(newState: UIState) {
state = newState
}
fun render(): String {
return when (state) {
is UIState.Loading -> "Loading..."
is UIState.Success -> "Data: ${state.data}"
is UIState.Error -> "Error: ${state.message}"
}
}
}
三、密封类子类 #
3.1 数据类子类 #
kotlin
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val code: Int, val message: String) : Result()
}
3.2 对象子类 #
kotlin
sealed class Permission {
object Granted : Permission()
object Denied : Permission()
}
3.3 普通类子类 #
kotlin
sealed class Shape {
class Circle(val radius: Double) : Shape()
class Rectangle(val width: Double, val height: Double) : Shape()
}
3.4 混合类型 #
kotlin
sealed class Expr {
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
}
四、when 表达式 #
4.1 完整覆盖 #
kotlin
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
}
fun handle(result: Result): String = when (result) {
is Result.Success -> "Success: ${result.data}"
is Result.Error -> "Error: ${result.message}"
// 不需要 else 分支,编译器会检查是否覆盖所有情况
}
4.2 新增子类时编译器提示 #
kotlin
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result() // 新增子类
}
fun handle(result: Result): String = when (result) {
is Result.Success -> "Success: ${result.data}"
is Result.Error -> "Error: ${result.message}"
// 编译器会提示缺少 Loading 分支
}
五、密封接口 #
5.1 定义密封接口 #
kotlin
sealed interface Action
object Start : Action
object Stop : Action
data class Execute(val command: String) : Action
5.2 实现密封接口 #
kotlin
sealed interface Shape {
val area: Double
}
data class Circle(val radius: Double) : Shape {
override val area: Double = Math.PI * radius * radius
}
data class Rectangle(val width: Double, val height: Double) : Shape {
override val area: Double = width * height
}
六、实战示例 #
6.1 网络请求状态 #
kotlin
sealed class NetworkState<out T> {
object Idle : NetworkState<Nothing>()
object Loading : NetworkState<Nothing>()
data class Success<T>(val data: T) : NetworkState<T>()
data class Error(val exception: Throwable) : NetworkState<Nothing>()
}
fun <T> handleState(state: NetworkState<T>) {
when (state) {
is NetworkState.Idle -> println("Idle")
is NetworkState.Loading -> println("Loading...")
is NetworkState.Success -> println("Success: ${state.data}")
is NetworkState.Error -> println("Error: ${state.exception.message}")
}
}
6.2 表达式计算器 #
kotlin
sealed class Expr {
data class Const(val value: Double) : Expr()
data class Sum(val left: Expr, val right: Expr) : Expr()
data class Mul(val left: Expr, val right: Expr) : Expr()
data class Div(val left: Expr, val right: Expr) : Expr()
}
fun eval(expr: Expr): Double = when (expr) {
is Expr.Const -> expr.value
is Expr.Sum -> eval(expr.left) + eval(expr.right)
is Expr.Mul -> eval(expr.left) * eval(expr.right)
is Expr.Div -> {
val right = eval(expr.right)
if (right != 0.0) eval(expr.left) / right else Double.NaN
}
}
val expr = Expr.Sum(
Expr.Const(1.0),
Expr.Mul(Expr.Const(2.0), Expr.Const(3.0))
)
println(eval(expr)) // 7.0
6.3 支付方式 #
kotlin
sealed class PaymentMethod {
data class CreditCard(
val number: String,
val expiryDate: String
) : PaymentMethod()
data class PayPal(val email: String) : PaymentMethod()
data class BankTransfer(
val accountNumber: String,
val routingNumber: String
) : PaymentMethod()
}
fun processPayment(method: PaymentMethod, amount: Double): String {
return when (method) {
is PaymentMethod.CreditCard ->
"Processing $amount via Credit Card ending in ${method.number.takeLast(4)}"
is PaymentMethod.PayPal ->
"Processing $amount via PayPal (${method.email})"
is PaymentMethod.BankTransfer ->
"Processing $amount via Bank Transfer"
}
}
6.4 导航事件 #
kotlin
sealed class NavigationEvent {
data class ToDetail(val id: Int) : NavigationEvent()
data class ToProfile(val userId: String) : NavigationEvent()
object Back : NavigationEvent()
data class ToUrl(val url: String) : NavigationEvent()
}
fun navigate(event: NavigationEvent) {
when (event) {
is NavigationEvent.ToDetail ->
println("Navigate to detail: ${event.id}")
is NavigationEvent.ToProfile ->
println("Navigate to profile: ${event.userId}")
is NavigationEvent.Back ->
println("Go back")
is NavigationEvent.ToUrl ->
println("Open URL: ${event.url}")
}
}
七、密封类 vs 枚举类 #
7.1 枚举类 #
kotlin
enum class State {
LOADING, SUCCESS, ERROR
}
7.2 密封类 #
kotlin
sealed class State {
object Loading : State()
data class Success(val data: String) : State()
data class Error(val message: String) : State()
}
7.3 区别 #
| 特性 | 枚举类 | 密封类 |
|---|---|---|
| 实例数量 | 固定一个 | 可以有多个 |
| 状态 | 单例 | 可以携带数据 |
| 继承 | 不能继承 | 可以继承 |
| 接口 | 可以实现 | 可以实现 |
7.4 选择建议 #
kotlin
// 使用枚举:固定状态,无数据
enum class Direction { NORTH, SOUTH, EAST, WEST }
// 使用密封类:有数据的状态
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val code: Int) : Result()
}
八、最佳实践 #
8.1 命名规范 #
kotlin
// 推荐:状态后缀
sealed class UIState { }
sealed class NetworkResult { }
// 推荐:事件后缀
sealed class NavigationEvent { }
8.2 使用 data class 携带数据 #
kotlin
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
}
8.3 使用 object 表示无数据状态 #
kotlin
sealed class State {
object Loading : State()
object Idle : State()
data class Data(val value: String) : State()
}
九、总结 #
密封类要点:
| 特性 | 说明 |
|---|---|
| 受限继承 | 子类必须在同一文件 |
| when 完整性 | 编译器检查覆盖 |
| 携带数据 | 子类可以携带数据 |
| 状态管理 | 适合表示有限状态 |
下一步,让我们学习 枚举类!
最后更新:2026-03-27