Kotlin 反射 - 检索属性(如果存在)

问题描述 投票:0回答:1

如果引用的对象有一个属性,我想检索一个属性。 (我有一个更复杂的用例,但这很好地说明了问题)。 看来,做到这一点的唯一方法是打破方差。 有安全的方法吗?

fun <T : Any> T.valueFor(name: String): Any? {
    val clazz = this::class as KClass<T> // <-- unsafe to get around variance
    val prop = clazz.memberProperties.firstOrNull { name == it.name }
        ?: throw IllegalArgumentException("No property called '$name'")
    return prop.get(this) // <-- compile error here without the cast
}

这是关于该主题的精彩讨论: 没有反射的 Kotlin 属性委托

我可以通过传入所需类型的 KClass 来解决这个问题,但整个想法是编写一个事先不知道类型的通用方法。 我正在尝试考虑一个反例,这将是一件坏事,特别是考虑到我们的数据模型是扁平的(没有继承)。

@Test
fun `retrieve property`() {
    open class Animal(val color: String)
    class Dog(val name: String, color: String) : Animal(color)

    val dog = Dog("Rocky", "Yellow")
    assertThat(dog.valueFor("name")).isEqualTo(dog.name)
    assertThat(dog.valueFor("color")).isEqualTo(dog.color)

    val animal: Animal = dog
    assertThat(animal.valueFor("name")).isEqualTo(dog.name)
    assertThat(animal.valueFor("color")).isEqualTo(dog.color)

    val diffAnimal = Animal("Orange")
    assertThrows<IllegalArgumentException> { diffAnimal.valueFor("name") }
    assertThat(diffAnimal.valueFor("color")).isEqualTo(diffAnimal.color)
}
kotlin kotlin-reflect
1个回答
0
投票

如果您打算将此扩展用于任何接收器,则可以将

<T : Any> T.valueFor
替换为
Any.valueFor

正如您已经评论的那样,

prop.get(this)
无法编译。这里我们需要使用
kotlin.reflect.KCallable#call

重写代码

import kotlin.reflect.full.memberProperties

fun Any.valueFor(name: String): Any? { // changed to Any.valueFor
    val clazz = this::class // Get the runtime type
    val prop = clazz.memberProperties.firstOrNull { it.name == name }
        ?: throw IllegalArgumentException("No property called '$name'")
    return prop.call(this) // Use `call` instead of `get`
}

代码在我的电脑上测试过。

© www.soinside.com 2019 - 2024. All rights reserved.