Kotlin copy 函数

350 阅读3分钟

在 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 更新状态,避免副作用。
  • 注意浅拷贝陷阱‌:嵌套对象需手动处理深拷贝。