在Kotlin中合并/合并数据类

有没有一种方法来合并kotlin数据类而不指定所有的属性?

data class MyDataClass(val prop1: String, val prop2: Int, ...//many props) 

具有以下签名的功能:

 fun merge(left: MyDataClass, right: MyDataClass): MyDataClass 

这个函数检查两个类的每个属性,以及它们在哪里不同,使用左边的参数来创建一个新的MyDataClass。

这可能使用kotlin-reflect或其他方法吗?

编辑:更清晰

这是我想要做的更好的描述

  data class Bob( val name: String?, val age: Int?, val remoteId: String?, val id: String) @Test fun bob(){ val original = Bob(id = "local_id", name = null, age = null, remoteId = null) val withName = original.copy(name = "Ben") val withAge = original.copy(age = 1) val withRemoteId = original.copy(remoteId = "remote_id") //TODO: merge without accessing all properties // val result = assertThat(result).isEqualTo(Bob(id = "local_id", name = "Ben", age=1, remoteId = "remote_id")) } 

 infix fun <T : Any> T.merge(mapping: KProperty1<T, *>.() -> Any?): T { //data class always has primary constructor ---v val constructor = this::class.primaryConstructor!! //calculate the property order val order = constructor.parameters.mapIndexed { index, it -> it.name to index } .associate { it }; // merge properties @Suppress("UNCHECKED_CAST") val merged = (this::class as KClass<T>).declaredMemberProperties .sortedWith(compareBy{ order[it.name]}) .map { it.mapping() } .toTypedArray() return constructor.call(*merged); } 

编辑

 infix fun <T : Any> T.merge(right: T): T { val left = this; return left merge mapping@ { // v--- implement your own merge strategy return@mapping this.get(left) ?: this.get(right); }; } 

 val original = Bob(id = "local_id", name = null, age = null, remoteId = null) val withName = original.copy(name = "Ben") val withAge = original.copy(age = 1) val withRemoteId = original.copy(remoteId = "remote_id") val result = withName merge withAge merge withRemoteId; 

如果你想从右边的值复制左边的null那么你可以做到以下几点:

 infix inline fun <reified T : Any> T.merge(other: T): T { val nameToProperty = T::class.declaredMemberProperties.associateBy { it.name } val primaryConstructor = T::class.primaryConstructor!! val args = primaryConstructor.parameters.associate { parameter -> val property = nameToProperty[parameter.name]!! parameter to (property.get(this) ?: property.get(other)) } return primaryConstructor.callBy(args) } 

用法:

 data class MyDataClass(val prop1: String?, val prop2: Int?) val a = MyDataClass(null, 1) val b = MyDataClass("b", 2) val c = a merge b // MyDataClass(prop1=b, prop2=1) 

您的要求与复制left值完全相同:

 fun merge(left: MyDataClass, right: MyDataClass) = left.copy() 

也许一个使用是不正确的了解另一个。 请详细说明,如果这不是你想要的。

请注意,因为没有使用right ,你可以让它变成可变的和“合并”多达你喜欢:)

 fun merge(left: MyDataClass, vararg right: MyDataClass) = left.copy() val totallyNewData = merge(data1, data2, data3, data4, ...) 

编辑

Kotlin的课程不跟踪他们的三角洲。 想想你正在经历这个过程。 第一次改变后,你有

 current = Bob("Ben", null, null, "local_id") next = Bob(null, 1, null, "local_id") 

它应该如何知道你希望next将变化应用于age而不是name ? 如果你只是基于可空性进行更新,@mfulton有一个很好的答案。 否则,你需要自己提供信息。