使用反射在 Kotlin 中按字符串设置 属性?
Set property by string in Kotlin using reflection?
我有一个对象:
class User {
var id: String? = null
var name: String? = null
}
和配对列表:
val fieldsToChange = listOf<Pair<String, String>>(Pair("name", "foo"), Pair("id", "bar"))
我想遍历成对列表并使用反射为给定属性设置适当的值。
给定 class 个实例 obj
我们可以使用 obj::class.memberProperties
.
提取具有名称的属性
我们可以构造一个从属性名字到属性:
的映射
val nameToProperty = obj::class.memberProperties.associateBy(KProperty<*>::name)
然后我们可以迭代 fieldsToChange
并检索 属性 并设置它:
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
}
此外,我们可以将其设为通用:
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")
可能的改进是优化 nameToProperty
以仅包含已转换为 KMutableProperty
的 MutableProperties
您可以使用地图作为代表:
class User(map: Map<String, String>) {
val id: String by map
val name: String by map
}
val fieldsToChange = listOf(Pair("name", "foo"), Pair("id", "bar"))
val map = fieldsToChange.map { it.first to it.second }.toMap()
val user = User(map)
我有一个对象:
class User {
var id: String? = null
var name: String? = null
}
和配对列表:
val fieldsToChange = listOf<Pair<String, String>>(Pair("name", "foo"), Pair("id", "bar"))
我想遍历成对列表并使用反射为给定属性设置适当的值。
给定 class 个实例 obj
我们可以使用 obj::class.memberProperties
.
我们可以构造一个从属性名字到属性:
的映射val nameToProperty = obj::class.memberProperties.associateBy(KProperty<*>::name)
然后我们可以迭代 fieldsToChange
并检索 属性 并设置它:
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
}
此外,我们可以将其设为通用:
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")
可能的改进是优化 nameToProperty
以仅包含已转换为 KMutableProperty
您可以使用地图作为代表:
class User(map: Map<String, String>) {
val id: String by map
val name: String by map
}
val fieldsToChange = listOf(Pair("name", "foo"), Pair("id", "bar"))
val map = fieldsToChange.map { it.first to it.second }.toMap()
val user = User(map)