kotlin中的data class及其应用

Kotlin是现在我们做新版本的Android项目首选的开发语言,其中的 data class 相较于其他编程语言有所不同,深刻理解对于我们提高开发原生App的效率有很大的帮助。

1. 基本概念

data class 是 Kotlin 中一种特殊的类,主要用于存储数据。编译器会自动为 data class 生成一些常用的函数,减少样板代码。

1
data class PhotoTaken(val photoInfo: String, val photo_filename: String) : Event()

2. 自动生成的函数

当声明一个 data class 时,Kotlin 编译器会自动生成以下函数:

基础函数

  • equals()hashCode() - 用于对象比较和哈希计算
  • toString() - 返回包含所有属性的字符串表示
  • copy() - 创建对象副本
  • componentN() 函数 - 支持解构声明

示例演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
data class Person(val name: String, val age: Int)

val person1 = Person("张三", 25)
val person2 = Person("张三", 25)
val person3 = Person("李四", 30)

// equals() 和 hashCode()
println(person1 == person2) // true
println(person1.hashCode() == person2.hashCode()) // true

// toString()
println(person1.toString()) // Person(name=张三, age=25)

// copy()
val person4 = person1.copy(age = 26)
println(person4) // Person(name=张三, age=26)

// 解构声明
val (name, age) = person1
println("姓名: $name, 年龄: $age") // 姓名: 张三, 年龄: 25

同时,也可以通过data class的属性直接访问其值:

1
2
println(person1.name) // 张三
println(person1.age) // 25

更详细的举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
data class Person(val name: String, val age: Int, var address: String)

val person = Person("张三", 25, "北京市")

// 直接访问属性(只读属性)
println(person.name) // 输出: 张三
println(person.age) // 输出: 25

// 直接访问和修改可变属性
println(person.address) // 输出: 北京市
person.address = "上海市" // 修改 var 属性
println(person.address) // 输出: 上海市

// 编译错误示例 - 无法修改只读属性
// person.name = "李四" // 编译错误:Val cannot be reassigned

3. 主要特性

主构造函数要求

data class 必须至少有一个主构造函数参数:

1
2
3
4
5
// 正确
data class User(val id: Int, val name: String)

// 错误 - 没有主构造函数参数
data class EmptyDataClass // 编译错误

属性可变性

支持 val(只读)和 var(可变)属性:

1
2
3
4
5
data class MutableUser(var id: Int, val name: String)

val user = MutableUser(1, "张三")
user.id = 2 // 可以修改 var 属性
// user.name = "李四" // 编译错误,val 属性不可修改

4. 实际应用场景

1. 数据传输对象 (DTO)

1
2
3
4
5
6
data class UserDto(
val id: Long,
val username: String,
val email: String,
val createdAt: String
)

2. 网络请求响应

1
2
3
4
5
data class ApiResponse<T>(
val code: Int,
val message: String,
val data: T?
)

3. 状态管理

1
2
3
4
5
// 在sealed class中
sealed class Event {
data class PhotoTaken(val photoInfo: String, val photo_filename: String) : Event()
data class LogMessage(val message: String) : Event()
}

4. 配置信息

1
2
3
4
5
data class AppConfig(
val apiUrl: String,
val timeout: Int,
val retryCount: Int
)

5. 高级用法

copy() 函数的使用

1
2
3
4
5
data class Product(val id: Int, val name: String, val price: Double, val category: String)

val product = Product(1, "手机", 2999.0, "电子产品")
val discountedProduct = product.copy(price = 2499.0) // 只修改价格
val renamedProduct = product.copy(name = "智能手机", category = "智能设备") // 修改多个属性

解构声明

1
2
3
4
5
6
7
8
9
10
11
data class Point(val x: Int, val y: Int)

val point = Point(10, 20)
val (x, y) = point // 解构
println("坐标: ($x, $y)") // 坐标: (10, 20)

// 在集合中使用
val points = listOf(Point(1, 2), Point(3, 4), Point(5, 6))
for ((x, y) in points) {
println("点: ($x, $y)")
}

与集合操作结合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
data class Student(val name: String, val score: Int)

val students = listOf(
Student("张三", 85),
Student("李四", 92),
Student("王五", 78)
)

// 过滤和映射
val highScorers = students.filter { it.score > 80 }
val names = students.map { it.name }

// 分组
val groupedByScore = students.groupBy { if (it.score >= 90) "优秀" else "一般" }

6. 注意事项和限制

1. 继承限制

1
2
3
4
5
6
// data class 可以继承其他类
open class BaseEntity(val id: Long)
data class User(override val id: Long, val name: String) : BaseEntity(id)

// 但不能被其他类继承
// class SpecialUser : User(1, "特殊用户") // 编译错误

2. 与密封类结合

1
2
3
4
sealed class Result<T> {
data class Success<T>(val data: T) : Result<T>()
data class Error<T>(val message: String) : Result<T>()
}

3. 注意重写函数

如果手动重写了自动生成的函数,编译器不会重新生成:

1
2
3
4
5
data class CustomToString(val name: String, val age: Int) {
override fun toString(): String {
return "Custom: $name ($age years old)"
}
}

7. 在您的项目中的应用

在我写的一个测试项目的 [UiHandler.kt] (代码在我的github或者gitee上)(file:///Users/test003/AndroidStudioProjects/testWifi001/app/src/main/java/com/example/testwifi001/UiHandler.kt) 中,data class 的使用体现了以下优势:

  1. 类型安全:每个事件类型都有明确的参数定义
  2. 简洁性:避免手动编写 getter、equals 等方法
  3. 可读性:一眼就能看出事件携带的数据
  4. 解构支持:可以在接收端方便地解构使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义事件
data class PhotoTaken(val photoInfo: String, val photo_filename: String) : Event()

// 发送事件
UiHandler.sendEvent(UiHandler.Event.PhotoTaken(photoInfo, filePath))

// 接收事件
override fun onUiEvent(event: UiHandler.Event) {
when (event) {
is UiHandler.Event.PhotoTaken -> {
// 直接访问属性
handlePhotoTaken(event.photoInfo, event.photo_filename)
}
}
}

这种设计模式在 Android 开发中非常常见,特别是在事件总线、状态管理、网络响应等场景中广泛使用。


kotlin中的data class及其应用
https://jycpp.github.io/21-10-23-kotlin中的data class及其应用.html
作者
Jet Yan
发布于
2021年10月23日
许可协议