Kotlin 数据类 #

一、数据类定义 #

1.1 基本语法 #

kotlin
data class User(val id: Int, val name: String, val email: String)

1.2 创建实例 #

kotlin
val user = User(1, "Kotlin", "kotlin@example.com")
println(user)  // User(id=1, name=Kotlin, email=kotlin@example.com)

1.3 数据类要求 #

  • 主构造函数至少有一个参数
  • 主构造函数参数必须是 valvar
  • 不能是 abstract、open、sealed、inner
kotlin
// 正确
data class User(val id: Int, val name: String)

// 错误
// data class Invalid(id: Int)  // 参数没有 val/var
// abstract data class AbstractUser(val id: Int)  // 不能是 abstract

二、自动生成的方法 #

2.1 equals 和 hashCode #

kotlin
data class User(val id: Int, val name: String)

val user1 = User(1, "Kotlin")
val user2 = User(1, "Kotlin")

println(user1 == user2)  // true(内容相等)
println(user1.hashCode() == user2.hashCode())  // true

2.2 toString #

kotlin
data class User(val id: Int, val name: String)

val user = User(1, "Kotlin")
println(user)  // User(id=1, name=Kotlin)

2.3 copy #

kotlin
data class User(val id: Int, val name: String, val email: String)

val user = User(1, "Kotlin", "kotlin@example.com")

// 复制并修改部分属性
val updated = user.copy(name = "Updated")
// User(id=1, name=Updated, email=kotlin@example.com)

// 完全复制
val same = user.copy()

2.4 componentN #

kotlin
data class User(val id: Int, val name: String)

val user = User(1, "Kotlin")

// 通过 componentN 访问
val id = user.component1()    // 1
val name = user.component2()  // "Kotlin"

三、解构声明 #

3.1 基本用法 #

kotlin
data class User(val id: Int, val name: String)

val user = User(1, "Kotlin")
val (id, name) = user

println("ID: $id, Name: $name")

3.2 在 for 循环中使用 #

kotlin
data class User(val id: Int, val name: String)

val users = listOf(
    User(1, "Alice"),
    User(2, "Bob"),
    User(3, "Charlie")
)

for ((id, name) in users) {
    println("$id: $name")
}

3.3 在 Map 中使用 #

kotlin
val map = mapOf("A" to 1, "B" to 2, "C" to 3)

for ((key, value) in map) {
    println("$key -> $value")
}

3.4 忽略某些值 #

kotlin
data class User(val id: Int, val name: String, val email: String)

val user = User(1, "Kotlin", "kotlin@example.com")

val (id, _, _) = user  // 使用 _ 忽略
println("ID: $id")

四、数据类与集合 #

4.1 在 List 中使用 #

kotlin
data class User(val id: Int, val name: String)

val users = listOf(
    User(1, "Alice"),
    User(2, "Bob"),
    User(1, "Alice")  // 重复
)

// 去重
val unique = users.distinct()
// [User(1, Alice), User(2, Bob)]

4.2 在 Set 中使用 #

kotlin
data class User(val id: Int, val name: String)

val users = setOf(
    User(1, "Alice"),
    User(2, "Bob"),
    User(1, "Alice")  // 自动去重
)

println(users.size)  // 2

4.3 作为 Map 的键 #

kotlin
data class Key(val id: Int, val type: String)

val map = mapOf(
    Key(1, "A") to "Value1",
    Key(2, "B") to "Value2"
)

map[Key(1, "A")]  // "Value1"

五、数据类继承 #

5.1 继承其他类 #

kotlin
open class BaseEntity(val createdAt: Long)

data class User(
    val id: Int,
    val name: String
) : BaseEntity(System.currentTimeMillis())

5.2 实现接口 #

kotlin
interface Identifiable {
    val id: Int
}

data class User(
    override val id: Int,
    val name: String
) : Identifiable

六、密封数据类 #

6.1 结合密封类 #

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) = when (result) {
    is Result.Success -> println("Data: ${result.data}")
    is Result.Error -> println("Error: ${result.message}")
    Result.Loading -> println("Loading...")
}

七、实战示例 #

7.1 API 响应 #

kotlin
data class ApiResponse<T>(
    val success: Boolean,
    val data: T?,
    val error: String?,
    val timestamp: Long = System.currentTimeMillis()
)

val response = ApiResponse(
    success = true,
    data = User(1, "Kotlin"),
    error = null
)

7.2 配置类 #

kotlin
data class DatabaseConfig(
    val host: String = "localhost",
    val port: Int = 5432,
    val name: String = "mydb",
    val user: String = "admin",
    val password: String = ""
)

val config = DatabaseConfig(
    host = "production-db",
    password = "secret"
)

7.3 坐标点 #

kotlin
data class Point(val x: Int, val y: Int) {
    fun move(dx: Int, dy: Int): Point {
        return copy(x = x + dx, y = y + dy)
    }
    
    fun distanceTo(other: Point): Double {
        val dx = x - other.x
        val dy = y - other.y
        return Math.sqrt((dx * dx + dy * dy).toDouble())
    }
}

val p1 = Point(0, 0)
val p2 = p1.move(3, 4)
println(p1.distanceTo(p2))  // 5.0

7.4 范围类 #

kotlin
data class DateRange(
    val start: LocalDate,
    val end: LocalDate
) {
    fun contains(date: LocalDate): Boolean {
        return date in start..end
    }
    
    val days: Long
        get() = ChronoUnit.DAYS.between(start, end) + 1
}

八、数据类 vs 普通类 #

8.1 代码对比 #

kotlin
// 数据类
data class User(val id: Int, val name: String)

// 等价的普通类
class NormalUser(val id: Int, val name: String) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is NormalUser) return false
        return id == other.id && name == other.name
    }
    
    override fun hashCode(): Int {
        var result = id
        result = 31 * result + name.hashCode()
        return result
    }
    
    override fun toString(): String {
        return "NormalUser(id=$id, name=$name)"
    }
    
    fun copy(id: Int = this.id, name: String = this.name): NormalUser {
        return NormalUser(id, name)
    }
}

8.2 选择建议 #

  • 需要存储数据、比较相等性 → 数据类
  • 需要复杂逻辑、可变状态 → 普通类

九、最佳实践 #

9.1 使用不可变属性 #

kotlin
// 推荐
data class User(val id: Int, val name: String)

// 不推荐
data class MutableUser(var id: Int, var name: String)

9.2 使用 copy 修改 #

kotlin
data class User(val id: Int, val name: String)

val user = User(1, "Kotlin")

// 推荐:使用 copy
val updated = user.copy(name = "Updated")

// 不推荐:使用 var
// user.name = "Updated"

9.3 合理设计属性 #

kotlin
// 推荐:包含所有需要比较的属性
data class User(val id: Int, val name: String, val email: String)

// 不推荐:包含不需要比较的属性
data class UserWithTimestamp(
    val id: Int,
    val name: String,
    val timestamp: Long  // 每次创建都不同,影响相等性比较
)

十、总结 #

数据类要点:

特性 说明
equals 内容比较
hashCode 哈希值计算
toString 字符串表示
copy 复制并修改
componentN 解构声明

下一步,让我们学习 密封类

最后更新:2026-03-27