Given a class with a bunch of members, I'd like to merge two instances of it. The resulting instance should keep the non-null values of each of the two inputs. If two non-null values contradict, an exception should be raised.
My current implementation somewhat works, but it does not scale well:
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) {
}
}
How can I avoid the manual repetition of each member (currently in mergeTwoThings
)?
Also, it would be nice if I would not need an unchecked cast (currently in mergeThingProperty
).
Aucun commentaire:
Enregistrer un commentaire