在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有一个很好的答案。 否则,你需要自己提供信息。