在 Kotlin 中,**copy 函数**是数据类(data class)自动生成的核心函数之一,用于高效创建对象的副本并选择性修改部分属性。以下是其核心用法和注意事项:
一、copy 函数的作用
- 快速复制对象:基于原对象创建新实例。
- 选择性修改属性:仅修改需要变更的属性,其余属性保持原值。
- 支持不可变数据:避免直接修改对象状态,符合函数式编程思想。
二、copy 函数的自动生成
Kotlin 编译器会为所有数据类(data class) 自动生成 copy 函数。例如:
data class User(val name: String, val age: Int)
生成的 copy 函数等效于:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
三、基本用法
1. 修改单个属性
val user1 = User("Alice", 30)
val user2 = user1.copy(age = 31) // 仅修改 age
println(user2) // 输出: User(name=Alice, age=31)
2. 修改多个属性
val user3 = user1.copy(name = "Bob", age = 25)
println(user3) // 输出: User(name=Bob, age=25)
3. 完全复制(无修改)
val user4 = user1.copy() // 创建完全相同的副本
println(user4 == user1) // 输出: true(内容相等)
println(user4 === user1) // 输出: false(不同对象引用)
四、高级用法
1. 深度复制(需手动实现)
默认 copy 是浅拷贝。若属性是对象引用,需手动处理嵌套复制:
data class Address(val city: String)
data class Person(val name: String, val address: Address)
val person1 = Person("Alice", Address("Beijing"))
val person2 = person1.copy(address = person1.address.copy(city = "Shanghai"))
println(person2) // 输出: Person(name=Alice, address=Address(city=Shanghai))
2. 与解构声明结合
val (name, age) = user1.copy(age = 35)
println("$name is $age years old") // 输出: Alice is 35 years old
五、注意事项
1. 仅数据类支持自动生成 copy
普通类(非 data class)需手动实现 copy:
class Animal(val name: String) {
// 手动实现 copy 函数
fun copy(name: String = this.name) = Animal(name)
}
2. 不可变性与 val/var
- 若数据类属性为
val(推荐),copy只能通过参数修改属性。 - 若属性为
var,直接修改原对象属性会破坏不可变性,应优先用copy。
3. 性能与对象创建
频繁调用 copy 可能产生大量临时对象,需权衡是否适合高性能场景。
六、copy 函数的实际应用场景
1. 状态更新(如 Redux 模式)
data class AppState(val isLoading: Boolean, val data: List<String>)
fun reduce(oldState: AppState, newData: List<String>): AppState {
return oldState.copy(isLoading = false, data = newData)
}
2. 配置对象复用
data class RequestConfig(
val timeout: Int = 5000,
val retries: Int = 3
)
val defaultConfig = RequestConfig()
val fastConfig = defaultConfig.copy(timeout = 2000)
3. 构建链式调用
val user = User("Alice", 30)
.copy(name = "Bob")
.copy(age = 25)
七、案例
package com.lhr.base.s5
data class KtBase91 (var name: String, var age: Int) // 主构造
{
var coreInfo : String = ""
init {
println("主构造被调用了")
}
// 次构造
constructor(name: String) : this(name, 99) {
println("次构造被调用")
coreInfo = "增加非常核心的内容信息"
}
override fun toString(): String {
return "toString name:$name, age:$age, coreInfo:$coreInfo"
}
}
/* 生成的toString 为什么只有两个参数?
答:默认生成的toString 或者 hashCode equals 等等... 主管主构造,不管次构造
public String toString() {
return "KtBase92(name=" + this.name + ", age=" + this.age + ")";
}
*/
fun main() {
val p1 = KtBase91("李元霸") // 调用次构造初始化对象
println(p1)
val newP2 = p1.copy("李连杰", 78)
println(newP2)
// copy toString hashCode equals 等等... 主管主构造,不管次构造
// 注意事项:使用copy的时候,由于内部代码只处理主构造,所以必须考虑次构造的内容
}
八、与 Java 对比
| 特性 | Kotlin copy 函数 | Java Clone 机制 |
|---|---|---|
| 语法简洁性 | 自动生成,无需手动实现 | 需实现 Cloneable 接口 |
| 类型安全 | 编译时检查参数类型 | 需处理 CloneNotSupportedException |
| 不可变支持 | 天然适合不可变数据 | 通常依赖深拷贝实现 |
总结
- 优先使用数据类:自动生成的
copy函数简化对象复制。 - 不可变数据设计:通过
copy更新状态,避免副作用。 - 注意浅拷贝陷阱:嵌套对象需手动处理深拷贝。