
在 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() // ❌ 错误:没有属性
- 主构造函数的参数必须是
val或var - 数据类不能是
abstract、open、sealed、inner - 可以实现接口,可以继承其他类
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())
}
1785

被折叠的 条评论
为什么被折叠?



