通过反射调用数据类copy()

问题描述 投票:0回答:3

Kotlin 中是否可以通过反射调用数据类的

copy()
函数?我怎样才能获得函数本身的引用?所有数据类都有一个超类吗?

reflection kotlin copy data-class
3个回答
22
投票

所有

data
类都没有共同的超类型。

基本上,

copy
是一个普通的成员函数,您可以使用Kotlin反射API调用它,如下所示:

val person = Person("Jane", 23)
val copy = person::class.memberFunctions.first { it.name == "copy" }
val instanceParam = copy.instanceParameter!!
val ageParam = copy.parameters.first { it.name == "age" }
val result = copy.callBy(mapOf(instanceParam to person, ageParam to 18))
println(result) // Person(name=Jane, age=18)

确保添加

kotlin-reflect
作为依赖项。

上面的示例显示了如何省略默认参数的值 - 不为

name
传递任何值。如果你想传递所有参数,可以用更简单的方式完成:

val person = Person("Jane", 23)
val copy = person::class.memberFunctions.first { it.name == "copy" }
val result = copy.call(person, person.name, 18)
println(result) // Person(name=Jane, age=18)

如果您传递所有参数的实参,Kotlin 反射 API 并不是调用函数所必需的,您也可以通过 Java 反射来做到这一点:

val person = Person("Jane", 23)
val copy = person::class.java.methods.first { it.name == "copy" }
val result = copy.invoke(person, person.name, 18)
println(result) // Person(name=Jane, age=18)

3
投票

因此,基于https://stackoverflow.com/users/2196460/hotkey上面的答案:

fun <T : Any> clone (obj: T): T {
  if (!obj::class.isData) {
    println(obj)
    throw Error("clone is only supported for data classes")
  }

  val copy = obj::class.memberFunctions.first { it.name == "copy" }
  val instanceParam = copy.instanceParameter!!
  return copy.callBy(mapOf(
    instanceParam to obj
  )) as T
}



0
投票

这是上述建议的另一个版本,它允许您调用

copy()
传递特定的属性值,就像直接调用 copy 时一样:

fun <T : Any> copyDataObject(toCopy: T, vararg properties: Pair<KProperty<*>, Any?>): T {
    val dataClass = toCopy::class
    require(dataClass.isData) { "Type of object to copy must be a data class" }
    val copyFunction = dataClass.memberFunctions.first { it.name == "copy" }
    val parameters = buildMap {
        put(copyFunction.instanceParameter!!, toCopy)
        properties.forEach { (property, value) ->
            val parameter = requireNotNull(
                copyFunction.parameters.firstOrNull { it.name == property.name }
            ) { "Parameter not found for property ${property.name}" }
            value?.let {
                require(
                    parameter.type.isSupertypeOf(it::class.starProjectedType)
                ) { "Incompatible type of value for property ${property.name}" }
            }
            put(parameter, value)
        }
    }
    @Suppress("UNCHECKED_CAST")
    return copyFunction.callBy(parameters) as T
}

有了这个,您应该能够执行以下操作:

val person = Person("Jane", 23, Sex.FEMALE)
val copy = copyDataObject(person, 
    person::name to "Jack",
    person::sex to Sex.MALE
)
println(person) // Person(name=Jane, age=23, sex=FEMALE)
println(copy) // Person(name=Jack, age=23, sex=MALE)

该函数还验证传递的对象是否是数据类(如果与您无关,请删除此行)以及传递的值是否与传递的属性兼容。

© www.soinside.com 2019 - 2024. All rights reserved.