使用反射在Kotlin中按字符串设置属性?

时间:2019-07-18 09:05:02

标签: kotlin reflection kotlin-reflect

我有一个对象:

class User {
  var id: String? = null
  var name: String? = null
}

和配对列表:

val fieldsToChange = listOf<Pair<String, String>>(Pair("name", "foo"), Pair("id", "bar"))

我想遍历成对的低谷列表,并使用反射为给定的属性设置适当的值。

2 个答案:

答案 0 :(得分:2)

给出类实例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的MutableProperty

答案 1 :(得分:0)

您可以使用地图作为委托:

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)