Kotlin Sealed 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的 sealed 关键字创建受限类层次结构,其中所有子类都必须在同一文件中声明。 这支持详尽的 when 表达式并更好地控制继承。 本教程通过实际示例探讨了 sealed 类和接口。
基本定义
sealed 类默认是抽象的,不能直接实例化。 所有子类都必须在同一文件中声明。 sealed 接口的工作方式类似,但允许在不同的文件中实现。 编译器知道所有可能的子类型。
基本 Sealed 类
最简单的 sealed 类定义了一个受限层次结构,其中所有子类在编译时都是已知的。 这对于表示一组固定的相关类型很有用。
package com.zetcode
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
}
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println("Success: ${result.data}")
is Result.Error -> println("Error: ${result.message}")
Result.Loading -> println("Loading...")
}
}
fun main() {
val success = Result.Success("Data loaded")
handleResult(success) // Output: Success: Data loaded
}
在这里,我们定义了一个 sealed 类 Result,它有三种可能的状态。 when 表达式是详尽的,因为所有可能的子类都是已知的。 这确保我们处理所有情况,而无需 else 分支。
具有属性的 Sealed 类
Sealed 类可以具有所有子类继承的属性。 这允许共享公共数据,同时通过层次结构保持类型安全。
package com.zetcode
sealed class Vehicle(val wheels: Int) {
class Car : Vehicle(4)
class Bike : Vehicle(2)
class Truck : Vehicle(18)
}
fun describeVehicle(vehicle: Vehicle) {
println("This vehicle has ${vehicle.wheels} wheels")
}
fun main() {
val car = Vehicle.Car()
describeVehicle(car) // Output: This vehicle has 4 wheels
}
Vehicle sealed 类定义了一个公共 wheels 属性。 所有子类都继承此属性。 describeVehicle 函数可以安全地访问 wheels,因为知道所有 Vehicle 类型都具有此属性。
Sealed 接口
Kotlin 1.5 引入了 sealed 接口,它们的工作方式类似于 sealed 类,但允许在不同的文件中实现。 这提供了更大的灵活性。
package com.zetcode
sealed interface PaymentMethod {
val accountId: String
}
data class CreditCard(override val accountId: String, val cardNumber: String) : PaymentMethod
data class BankTransfer(override val accountId: String, val bankCode: String) : PaymentMethod
fun processPayment(method: PaymentMethod) {
println("Processing payment for account ${method.accountId}")
when (method) {
is CreditCard -> println("Using card ${method.cardNumber}")
is BankTransfer -> println("Using bank code ${method.bankCode}")
}
}
fun main() {
val payment = CreditCard("acc123", "4111111111111111")
processPayment(payment)
}
PaymentMethod sealed 接口定义了一个公共属性。 实现可以在不同的文件中。 when 表达式仍然是详尽的,因为编译器知道所有实现。 这将灵活性与类型安全相结合。
嵌套 Sealed 类
Sealed 类可以嵌套在其他类或接口中。 这有助于组织复杂的层次结构,同时保持 sealed 类的优势。
package com.zetcode
class ApiClient {
sealed class Response {
data class Success(val data: String) : Response()
data class Error(val code: Int) : Response()
}
fun getResponse(): Response {
return Response.Success("API data")
}
}
fun main() {
val client = ApiClient()
val response = client.getResponse()
when (response) {
is ApiClient.Response.Success -> println(response.data)
is ApiClient.Response.Error -> println("Error ${response.code}")
}
}
Response sealed 类嵌套在 ApiClient 中。 这将相关类型保持在一起,同时保持 sealed 类的优势。 when 表达式仍然可以是详尽的,因为所有子类都是已知的。
具有函数的 Sealed 类
Sealed 类可以定义子类可以重写的函数。 这允许多态行为,同时保持受限层次结构。
package com.zetcode
sealed class Shape {
abstract fun area(): Double
class Circle(val radius: Double) : Shape() {
override fun area() = Math.PI * radius * radius
}
class Square(val side: Double) : Shape() {
override fun area() = side * side
}
}
fun printArea(shape: Shape) {
println("Area: ${shape.area()}")
}
fun main() {
val circle = Shape.Circle(5.0)
printArea(circle) // Output: Area: 78.53981633974483
}
Shape sealed 类定义了一个抽象的 area 函数。 每个子类都提供自己的实现。 printArea 函数可以在任何 Shape 上调用 area(),知道所有子类都实现了它。 这将多态性与类型安全相结合。
详尽的 When 表达式
Sealed 类的主要好处之一是支持详尽的 when 表达式。 编译器确保处理所有情况,而无需 else 分支。
package com.zetcode
sealed class NetworkState {
object Connected : NetworkState()
object Disconnected : NetworkState()
data class Error(val message: String) : NetworkState()
}
fun handleNetwork(state: NetworkState) {
when (state) {
NetworkState.Connected -> println("Connected")
NetworkState.Disconnected -> println("Disconnected")
is NetworkState.Error -> println("Error: ${state.message}")
}
}
fun main() {
val state = NetworkState.Connected
handleNetwork(state) // Output: Connected
}
when 表达式不需要 else 分支,因为处理了所有可能的 NetworkState 子类。 如果我们稍后添加一个新的子类,编译器将标记未处理的情况。 这使代码更易于维护和更安全。
具有伴生对象的 Sealed 类
Sealed 类可以具有带有工厂方法的伴生对象。 这提供了一种干净的方式来创建实例,同时保持 sealed 层次结构。
package com.zetcode
sealed class UserResult {
data class Success(val user: String) : UserResult()
data class Failure(val reason: String) : UserResult()
companion object {
fun success(user: String) = Success(user)
fun failure(reason: String) = Failure(reason)
}
}
fun main() {
val result = UserResult.success("john_doe")
when (result) {
is UserResult.Success -> println("User: ${result.user}")
is UserResult.Failure -> println("Failed: ${result.reason}")
}
}
UserResult sealed 类在其伴生对象中提供了工厂方法。 这封装了子类的创建,同时保持了 sealed 层次结构的优势。 when 表达式仍然是详尽的,因为所有子类都是已知的。
Sealed 类的最佳实践
- 用于受限层次结构:当您需要一组固定的相关类型并且这些类型不会经常更改时。
- 利用详尽的 when:利用编译器检查来处理所有情况。
- 将子类保留在同一文件中:对于 sealed 类(sealed 接口不需要)。
- 更喜欢数据类/对象:对于层次结构中的叶节点,以获得有用的实现。
- 与多态性结合:在 sealed 类中使用抽象函数来实现多态行为。
来源
本教程深入介绍了 Kotlin 的 sealed 关键字,展示了如何创建受限类层次结构。 我们探讨了 sealed 类、sealed 接口及其好处,例如详尽的 when 表达式。 正确使用 sealed 类型可以使您的代码更类型安全且更易于维护。
作者
列出 所有 Kotlin 教程。