Учитывая класс с кучей членов, я хотел бы объединить два его экземпляра. Результирующий экземпляр должен сохранять ненулевые значения каждого из двух входов. Если два ненулевых значения противоречат друг другу, должно возникнуть исключение.
Моя текущая реализация работает, но плохо масштабируется:
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
import kotlin.test.fail
class Thing(
val a: Int,
var b: String,
val c: Int? = null,
val d: Boolean? = null,
val e: Long? = null,
val f: String? = null,
val g: String? = null
)
private fun <T> mergeThingProperty(property: KProperty1<Thing, *>, a: Thing, b: Thing): T {
val propA = property.get(a)
val propB = property.get(b)
val mergedValue = if (propA != null && propB == null) {
propA
} else if (propA == null && propB != null) {
propB
} else if (propA != null && propB != null) {
if (propA != propB) {
throw RuntimeException("Can not merge Thing data on property ${property.name}: $propA vs. $propB.")
} else {
propA
}
} else {
null
}
@Suppress("UNCHECKED_CAST")
return mergedValue as T
}
fun mergeTwoThings(thing1: Thing, thing2: Thing): Thing {
val properties = Thing::class.memberProperties.associateBy { it.name }
val propertyMissingMsg = "Missing value in Thing properties"
return Thing(
mergeThingProperty(properties["a"] ?: error(propertyMissingMsg), thing1, thing2),
mergeThingProperty(properties["b"] ?: error(propertyMissingMsg), thing1, thing2),
mergeThingProperty(properties["c"] ?: error(propertyMissingMsg), thing1, thing2),
mergeThingProperty(properties["d"] ?: error(propertyMissingMsg), thing1, thing2),
mergeThingProperty(properties["e"] ?: error(propertyMissingMsg), thing1, thing2),
mergeThingProperty(properties["f"] ?: error(propertyMissingMsg), thing1, thing2),
mergeThingProperty(properties["g"] ?: error(propertyMissingMsg), thing1, thing2)
)
}
fun main() {
val result1 = mergeTwoThings(Thing(a = 42, b = "foo"), Thing(a = 42, b = "foo", c = 23))
assert(result1.c == 23)
assert(result1.d == null)
try {
mergeTwoThings(Thing(a = 42, b = "foo"), Thing(a = 42, b = "bar"))
fail("An exception should have been thrown.")
} catch (ex: RuntimeException) {
}
}
Как избежать повторения вручную каждый член (в настоящее время находится в mergeTwoThings
)?
Кроме того, было бы неплохо, если бы мне не требовалось непроверенное приведение (в настоящее время в mergeThingProperty
).