Kotlin 数据类与密封类
Kotlin 数据类与密封类(2025年中文讲解)
Kotlin 的数据类(Data Class)和密封类(Sealed Class)是两种强大的类类型,专为特定场景设计,简化代码并增强类型安全。数据类用于存储数据,自动生成常用方法(如 toString
、equals
),而密封类用于限制类层次,适合建模有限状态或类型,广泛应用于 Android(Jetpack Compose)、后端(Spring Boot)和 Kotlin Multiplatform(KMP)项目。2025年,Kotlin 2.0(K2 编译器)优化了数据类和密封类的性能,尤其在 KMP 和 Compose 中应用广泛。本教程详细讲解两者的语法、用法和实践,基于官方文档、CSDN 和知乎,适合初学者和开发者。建议用 Kotlin Playground(https://play.kotlinlang.org/)练习。
一、数据类(Data Class)
1. 数据类概览(必知)
- 定义:用
data class
关键字,专为存储数据设计,自动生成toString
、equals
、hashCode
和copy
方法。 - 特点:
- 简洁:无需手动实现 getter/setter 或
toString
。 - 不可继承:数据类默认
final
,不可用open
继承。 - 空安全:支持可空类型(
?
)。 - 用途:JSON 解析、数据库实体、UI 数据模型。
- 2025年趋势:数据类在 Jetpack Compose 和 KMP 中占主导,用于传递 UI 状态或跨平台数据。
2. 数据类语法与用法
- 基本语法:主构造函数至少一个参数,参数需为
val
或var
。
data class User(val id: Int, val name: String)
fun main() {
val user = User(1, "Alice")
println(user) // 输出:User(id=1, name=Alice)
println(user.name) // 输出:Alice
val user2 = user.copy(name = "Bob") // 复制并修改
println(user2) // 输出:User(id=1, name=Bob)
println(user == user2) // 输出:false(比较内容)
}
- 自动生成方法:
toString()
:格式化输出对象。equals()
和hashCode()
:基于主构造函数属性比较。copy()
:创建对象副本,可修改部分属性。- 解构声明:
val (id, name) = user
。 - 解构声明:
fun main() {
val user = User(1, "Alice")
val (id, name) = user // 解构
println("ID: $id, Name: $name") // 输出:ID: 1, Name: Alice
}
- 空安全:
data class Product(val id: Int?, val name: String?)
fun main() {
val product = Product(null, null)
println(product) // 输出:Product(id=null, name=null)
val safeName = product.name ?: "Unknown"
println(safeName) // 输出:Unknown
}
- 添加方法:
data class Student(val id: Int, val name: String) {
fun greet() = println("Hello, $name!")
}
fun main() {
val student = Student(1, "Bob")
student.greet() // 输出:Hello, Bob!
}
3. 注意事项
- 限制:
- 主构造函数至少一个参数。
- 参数必须是
val
或var
(非临时变量)。 - 不能用
open
(不可继承)。 - 最佳实践:
- 用于简单数据模型,避免复杂逻辑。
- 优先
val
参数,确保不可变性。 - 结合空安全(
?
和?:
)处理可空数据。 - 性能:Kotlin 2.0 优化
copy
和equals
性能,适合高频使用。
二、密封类(Sealed Class)
1. 密封类概览(必知)
- 定义:用
sealed class
关键字,限制类的继承层次,所有子类必须定义在同一文件或模块内。 - 特点:
- 类型安全:适合建模有限状态(如 API 响应、UI 状态)。
- 与 when 结合:
when
表达式可穷尽所有子类,无需else
。 - 可继承:子类可以是普通类、数据类或其他密封类。
- 用途:状态机、枚举增强、KMP 跨平台逻辑。
- 2025年趋势:密封类在 Jetpack Compose(UI 状态管理)和 KMP(平台特定逻辑)中广泛使用。
2. 密封类语法与用法
- 基本语法:
sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
fun processResult(result: Result) = when (result) {
is Success -> println("Success: ${result.data}")
is Error -> println("Error: ${result.message}")
is Loading -> println("Loading...")
}
fun main() {
processResult(Success("Data loaded")) // 输出:Success: Data loaded
processResult(Error("Failed")) // 输出:Error: Failed
processResult(Loading) // 输出:Loading...
}
- 说明:
- 子类(
Success
、Error
、Loading
)必须在同一文件或模块。 when
表达式自动穷尽所有子类,无需else
。- 嵌套密封类:
sealed class Operation {
sealed class Math : Operation() {
data class Add(val a: Int, val b: Int) : Math()
data class Subtract(val a: Int, val b: Int) : Math()
}
object Reset : Operation()
}
fun execute(op: Operation) = when (op) {
is Operation.Math.Add -> println("Result: ${op.a + op.b}")
is Operation.Math.Subtract -> println("Result: ${op.a - op.b}")
is Operation.Reset -> println("Reset")
}
fun main() {
execute(Operation.Math.Add(5, 3)) // 输出:Result: 8
execute(Operation.Reset) // 输出:Reset
}
- 空安全:
sealed class Response
data class Success(val data: String?) : Response()
data class Error(val error: String?) : Response()
fun handleResponse(response: Response) = when (response) {
is Success -> println("Data: ${response.data ?: "No data"}")
is Error -> println("Error: ${response.error ?: "Unknown error"}")
}
fun main() {
handleResponse(Success(null)) // 输出:Data: No data
handleResponse(Error("Timeout")) // 输出:Error: Timeout
}
3. 注意事项
- 限制:
- 子类必须在同一文件或模块(Kotlin 1.5+ 放宽为模块内)。
- 密封类不能实例化(抽象类)。
- 最佳实践:
- 用于有限状态(如 API 响应、UI 状态)。
- 结合
when
确保类型安全。 - 子类用
data class
或object
简化实现。 - 性能:Kotlin 2.0 优化
when
匹配密封类的字节码,效率提升。
三、实践示例(综合应用)
- 命令行示例(API 响应处理):
sealed class ApiResponse
data class Success(val data: String) : ApiResponse()
data class Error(val message: String) : ApiResponse()
object Loading : ApiResponse()
fun processResponse(response: ApiResponse) = when (response) {
is Success -> println("Data: ${response.data}")
is Error -> println("Error: ${response.message}")
is Loading -> println("Loading...")
}
fun main() {
val responses = listOf(
Success("User data"),
Error("Network failure"),
Loading
)
responses.forEach { processResponse(it) }
}
输出:
Data: User data
Error: Network failure
Loading...
- Android 示例(UI 状态管理):
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
sealed class UiState
data class Loaded(val user: User) : UiState()
data class Error(val message: String) : UiState()
object Loading : UiState()
data class User(val id: Int, val name: String)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView: TextView = findViewById(R.id.textView)
val button: Button = findViewById(R.id.button)
var state: UiState = Loading
button.setOnClickListener {
state = when (state) {
is Loading -> Loaded(User(1, "Alice"))
is Loaded -> Error("Failed to refresh")
is Error -> Loading
}
textView.text = when (state) {
is Loading -> "Loading..."
is Loaded -> "User: ${(state as Loaded).user.name}"
is Error -> "Error: ${(state as Error).message}"
}
}
}
}
布局(res/layout/activity_main.xml
):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press button" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change State" />
</LinearLayout>
功能:点击按钮循环切换 UI 状态(Loading → Loaded → Error),显示对应文本。
四、注意事项与最佳实践
- 数据类:
- 用途:适合简单数据模型(如 JSON、数据库实体)。
- 限制:不可继承,参数需
val
/var
。 - 空安全:优先用
?:
处理可空属性。 - 优化:Kotlin 2.0 减少
copy
开销,适合高频操作。
- 密封类:
- 用途:建模有限状态(如 API 响应、UI 状态)。
- 与 when 结合:确保穷尽所有子类,增强类型安全。
- 子类选择:用
data class
存储数据,object
表示单例状态。
- 2025年趋势:
- Jetpack Compose:数据类用于 UI 状态,密封类管理状态切换。
- KMP:密封类用
expect
/actual
实现跨平台逻辑:kotlin expect sealed class PlatformResponse
- AI 辅助:IntelliJ 的 Codeium 插件可生成数据类和密封类代码。
五、学习建议
- 练习:用 Kotlin Playground 实践数据类(复制/解构)和密封类(状态处理)。
- 资源:
- 官方文档:https://kotlinlang.org/docs/data-classes.html、https://kotlinlang.org/docs/sealed-classes.html
- B站:尚硅谷 Kotlin 教程(免费,包含数据类和密封类)。
- CSDN:搜索“Kotlin 数据类 密封类”。
- 时间:2-3 天掌握两者,1 周熟悉 Android/KMP 结合。
- 实践:开发小型 App(如用户列表、状态切换)。
六、总结
Kotlin 数据类和密封类是现代开发利器:
- 数据类:必知自动生成方法(
toString
、copy
),必会定义、解构和空安全处理,适合数据模型。 - 密封类:必知类型安全和
when
穷尽,必会定义子类和状态处理,适合有限状态建模。
2025年,Kotlin 2.0 提升性能,数据类和密封类在 Android(Compose)和 KMP 中广泛使用,相比 Java 更简洁、安全。
如果需要具体场景代码(如 Compose 状态管理或 KMP 示例)或有问题,告诉我,我可以提供更详细解答!