Kotlin中的深度合并数据类
如何对Kotlin中的两个数据类进行递归/深度合并?大概是这样的:Kotlin中的深度合并数据类,kotlin,reflection,data-class,Kotlin,Reflection,Data Class,如何对Kotlin中的两个数据类进行递归/深度合并?大概是这样的: import kotlin.reflect.* import kotlin.reflect.full.* data class Address( val street: String? = null, val zip: String? = null ) data class User( val name: String? = null, val age: Int? = null, val address:
import kotlin.reflect.*
import kotlin.reflect.full.*
data class Address(
val street: String? = null,
val zip: String? = null
)
data class User(
val name: String? = null,
val age: Int? = null,
val address: Address? = null
)
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]!!
val type = property.returnType.classifier as KClass<*>
if (type.isData) {
parameter to this.merge(other) //inline function can't be recursive
} else {
parameter to (property.get(other) ?: property.get(this))
}
}
return primaryConstructor.callBy(args)
}
val u1 = User(name = "Tiina", address = Address(street = "Hämeenkatu"))
val u2 = User(age = 23, address = Address(zip = "33100"))
u1.merge(u2)
// expected: User(age = 23, name= "Tiina", address = Address(zip = "33100", street = "Hämeenkatu")
导入kotlin.reflect*
导入kotlin.reflect.full*
数据类地址(
val street:字符串?=null,
val-zip:String?=null
)
数据类用户(
val名称:字符串?=null,
val年龄:Int?=null,
val地址:地址?=null
)
内联乐趣T.merge(其他:T):T{
val nameToProperty=T::class.declaredMemberProperties.associateBy{it.name}
val primaryConstructor=T::class.primaryConstructor!!
val args=primaryConstructor.parameters.associate{parameter->
val property=nametroperty[参数.名称]!!
val type=property.returnType.classifier作为KClass
if(类型为isData){
此参数的参数。merge(other)//内联函数不能是递归函数
}否则{
(property.get(其他)的参数:property.get(此))
}
}
返回primaryConstructor.callBy(args)
}
val u1=用户(name=“Tiina”,address=地址(street=“Hämeenkatu”))
val u2=用户(年龄=23,地址=address(zip=“33100”))
u1.合并(u2)
//预期:用户(年龄=23,姓名=Tiina,地址=address(zip=“33100”,street=“Hämeenkatu”)
相关:发布的代码中有几个问题
上的属性值合并时,调用了this
和other
,因此它变成了无休止的递归get
无法在KProperty1上使用import kotlin.reflect.KClass
导入kotlin.reflect.KParameter
导入kotlin.reflect.KProperty1
导入kotlin.reflect.full.declaredMemberProperties
导入kotlin.reflect.full.isSubclassOf
导入kotlin.reflect.full.primaryConstructor
数据类地址(
val street:字符串?=null,
val-zip:String?=null
)
数据类用户(
val名称:字符串?=null,
val年龄:Int?=null,
val地址:地址?=空,
val映射:映射?=null
)
fun mergeData(属性:KProperty1,左:T,右:T):有吗{
val leftValue=property.getter.call(左)
val rightValue=property.getter.call(右)
返回rightValue?让我们{
if((property.returnType.classifier作为KClass).isSubclassOf(Map::class))(leftValue作为?Map)?.plus(它作为Map)
else leftValue?合并(it)
}?:rightValue?:leftValue
}
fun lastNonNull(属性:KProperty1,左:T,右:T)=
property.getter.call(右)?:property.getter.call(左)
乐趣T.合并(其他:T):T{
val nameToProperty=this::class.declaredMemberProperties.associateBy{it.name}
val primaryConstructor=this::class.primaryConstructor!!
val args:Map=primaryConstructor.parameters.associateWith{parameter->
val property=nametroperty[参数.名称]!!
val type=property.returnType.classifier作为KClass
什么时候{
type.isData | | type.isSubclassOf(Map::class)->合并数据(属性、此、其他)
else->lastNonNull(属性、此、其他)
}
}
返回primaryConstructor.callBy(args)
}
//核实
val u1=用户(name=“Tiina”,address=地址(street=“Hämeenkatu”),map=mapOf(“a”到1))
val u2=用户(年龄=23岁,地址=address(zip=“33100”),地图=mapOf(“b”到2))
检查(
u1.合并(u2)=用户(
年龄=23岁,
name=“Tiina”,
地址=地址(zip=“33100”,street=“Hämeenkatu”),
map=mapOf(“a”到1,“b”到2)
)
) {
“不起作用”
}
println(“工作!”)
首先,你说的深度合并是什么意思?如果u1和u2都定义了一个名称,你预计会发生什么?它应该取u1或u2的名称吗?其次,你真的需要进行一次通用的“深度合并”(即适用于所有数据类型的深度合并)吗?如果你真的问自己“我现在需要这个超级通用的解决方案吗?”答案很可能是否定的。如果你真的需要一个通用的解决方案来深度合并2个对象,那么看起来你走的是一条比较合理的路线。如果两者都定义了一个字段,那么最后一个非空值将获胜。嵌套的数据类应该递归合并。在我的例子中,数据类层次结构很深,所以不需要要手动完成这项工作,需要一个通用的解决方案。来自Clojure,这里有一个动态深度合并:太棒了,谢谢!简单多了,很有效。一条注释:mergeValues
中的最后一行可能是:``返回rightValue?.let{r->leftValue?.let{l->r.merge(l))}?:r}?:leftValue```因此我们在这里也使用非空值。如果你认为这是正确的,你能为未来的求职者更新这个例子吗?是的,我错过了那个案例。更新了代码,尽管与建议的代码略有不同,以提高可读性。建议避免嵌套作用域函数过多。提高可读性,减少重构过程中出错的概率。我很高兴能帮上忙:)很好的解决方案。虽然我希望这能对地图起作用,但事实并非如此数据类用户(val name:String?=null,val items:Map?=null)
用户(items=mapOf(“monitor”到1))
用户(name=“George”,items=mapOf(“laptop”到2))
@emanuelgeorgeoategan支持的映射在这里是一个微不足道的补充,我也更新了答案来展示这一点。