在这里插入图片描述
在 Kotlin 中,data class 是一种专门用于存储数据(state)的类。自动为你生成一系列常用方法,比如:

  • toString(),字符串格式类似于"User(name=John, age=42)"
  • equals() / hashCode(),用属性值比较两个对象是否相等
  • copy()
  • componentN(),编译器自动生成形如:fun component1() = name(属性);fun component2() = age;…

基本语法

data class User(val name: String, val age: Int)

使用示例:

val user1 = User("Tom", 20)
val user2 = user1.copy(age = 25)
val user3 = User("Tom", 20)

println(user1)           // 输出:User(name=Tom, age=20)
println(user2)           // 输出:User(name=Tom, age=25)
println(user1 == user2)  // 输出:false
println(user1 == user3)  // 输出:true

val (n, a) = user1       // 解构
println("$n - $a")       // 输出:Tom - 20

语法规则

  • 主构造函数中至少要有一个参数
data class Empty() // ❌ 错误:没有属性
  • 主构造函数的参数必须是 valvar
  • 数据类不能是 abstractopensealedinner
  • 可以实现接口,可以继承其他类
data class Response(val code: Int, val msg: String): Serializable
  • 如果数据类中自动生成的函数存在显式实现(注意不允许为componentN()copy()提供显式实现),或者父类中有final实现(子类无法覆盖),则不会自动生成这些函数,而使用现有的实现。
data class Person(val name: String, val age: Int) {
    // 显式实现 toString()
    override fun toString(): String {
        return "Hello Person(name=$name, age=$age)"
    }
}

fun main() {
    val p1 = Person("Alice", 30)
    println(p1.toString()) // Hello Person(name=Alice, age=30)
}
open class Base {
    final override fun toString(): String {
        return "Base class"
    }
}

data class Derived(val id: Int) : Base()

fun main() {
    val d = Derived(1)
    println(d.toString())  // Base class
}
  • 如果父类中已经有了某个 componentN() 函数,是open 的,并且返回类型与数据类中该属性类型兼容,那么编译器会自动生成覆盖版本。否则,编译器无法生成覆盖版本,编译错误。
open class Base {
    // 如果是 final 则编译错误
    open fun component1(): String = "A"
    open fun component2(): String = "B"
    // 注意,即使写成 open fun component2(): Double = 1.2 也不会报错!!!
}

data class Derived(val name: String, val age: String) : Base()
// componentN 被重写,返回 name,age

fun main() {
    val d = Derived("Tom", "20")

    val (name, age) = d
    println("$name, $age")   // Tom, 20
}
  • 编译器只会根据【主构造函数】中定义的属性生成对应函数
data class Person(val name: String) {
    var age: Int = 0
}

name —— 定义在主构造函数中 ✅
→ 编译器会为它生成:

  • component1()
  • copy(name=...)
  • equals()hashCode()toString() 中参与比较与输出

age —— 定义在类体中(不是主构造函数)❌
→ 编译器不会为它生成:

  • componentN()
  • copy() 参数
  • equals() / hashCode() / toString() 的比较部分
fun main() {
    val p1 = Person("Alice")
    p1.age = 18

    val p2 = Person("Alice")
    p2.age = 25

    println(p1 == p2) // ✅ true —— 因为只比较 name
    println(p1.toString()) // ✅ Person(name=Alice)
    println(p1.copy()) // ✅ Person(name=Alice)

    // 解构
    val (n) = p1
    println(n) // ✅ Alice
}

关于copy

可以方便地复制一个对象并选择性修改某些属性,在保持不可变风格的同时,快速创建新对象。

data class User(val name: String, val age: Int)

// 编译器会自动生成类似下面的函数
fun copy(name: String = this.name, age: Int = this.age): User {
    return User(name, age)
}
fun main() {
    val u1 = User("Alice", 20)

    val u2 = u1.copy()
    println(u2) // User(name=Alice, age=20)
    
    println(u1 == u2)  // true
    println(u1.equals(u2))  // 与上面等价
    println(u1 === u2) // false

    // 修改一个字段
    val u3 = u1.copy(age = 25)
    println(u3) // User(name=Alice, age=25)
}

编译器生成的 copy() 方法只包含 主构造函数中的属性

data class Person(val name: String) {
    var age: Int = 18
}

val p1 = Person("Bob")
p1.age = 30

val p2 = p1.copy()
println(p2.age) // ⚠️ 输出 18,而不是 30!

不可变对象更新

data class State(val count: Int, val enabled: Boolean)

fun main() {
    val s1 = State(5, true)
    val s2 = s1.copy(count = s1.count + 1)

    println(s1) // State(count=5, enabled=true)
    println(s2) // State(count=6, enabled=true)
}

集合更新

data class User(val name: String, val age: Int)
val users = listOf(User("Tom", 18), User("Jerry", 20))
val updated = users.map {
    if (it.name == "Tom") it.copy(age = 19) else it
}
println(updated) // [User(name=Tom, age=19), User(name=Jerry, age=20)]

copy()equals() 的关系

  • copy() 创建的新对象会重新计算 hashCode()
  • copy() 生成的新对象与原对象比较时,如果属性值一致,则 equals() 返回 true
val u1 = User("Alice", 22)
val u2 = u1.copy()
println(u1 == u2) // ✅ true
println(u1 === u2) // ❌ false(引用不同)

copy()是浅拷贝,对于引用类型属性只会复制它们的引用地址

data class Book(val title: String, val tags: MutableList<String>)

val b1 = Book("Kotlin", mutableListOf("programming"))
val b2 = b1.copy()

b2.tags.add("advanced")
println(b2.tags) // ["programming", "advanced"]
println(b1.tags) // ⚠️ ["programming", "advanced"]

// tags是一个引用类型,copy只是复制了引用。如果你需要完全独立的副本,需要自己实现深拷贝。
data class Book(val title: String, val tags: MutableList<String>) {
    fun deepCopy() = Book(title, tags.toMutableList())
}
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐