I have an object:
class User {
var id: String? = null
var name: String? = null
}
and list of pairs:
val fieldsToChange = listOf<Pair<String, String>>(Pair("name", "foo"), Pair("id", "bar"))
I would like to iterate trough list of pairs and set appropriate values for given properties using reflection.
Given class instance obj
we can extract properties with names using obj::class.memberProperties
.
We can construct a mapping from property name to property:
val nameToProperty = obj::class.memberProperties.associateBy(KProperty<*>::name)
Then we can iterate over fieldsToChange
and retrieve property and set it:
fieldsToChange.forEach { (propertyName, propertyValue) ->
nameToProperty[propertyName]
.takeIf { it is KMutableProperty<*> } // take only "settable" (var) properties
?.let { it as KMutableProperty<*> } // cast it to mutable property so we can access setter
?.let { it.setter.call(obj, propertyValue) } // call the setter
}
Additionally, we can make this generic:
fun setFields(obj: Any, fieldsToChange: List<Pair<String, Any?>>) {
val nameToProperty = obj::class.memberProperties.associateBy(KProperty<*>::name)
fieldsToChange.forEach { (propertyName, propertyValue) ->
nameToProperty[propertyName]
.takeIf { it is KMutableProperty<*> }
?.let { it as KMutableProperty<*> }
?.let { it.setter.call(obj, propertyValue) }
}
}
val user = User()
setFields(user, fieldsToChange)
assert(user.name == "foo")
assert(user.id == "bar")
Possible improvement would be to optimize nameToProperty
to contain only MutableProperties already casted to KMutableProperty