如何通过反射获取属性的值

时间:2017-09-12 18:02:49

标签: reflection kotlin

我想使用具有一些注释的所有属性的值。在大多数情况下,我的代码都有效,我得到了所有属性,只接受了那些带有注释的属性。

private inline fun <reified A : Annotation> (target: Any) {
    target::class.memberProperties
                 .filter { it.annotations.any { annotation -> annotation is A } }
                 .forEach {
                     // How do I get the value here?
                 }
}

我想使用it.get(...),但get需要Nothing作为参数。同样适用于getter。调用it.call(target)确实有效但看起来不对,因为有一个我不知道如何调用的实际get

那么获取属性值的正确方法是什么?

1 个答案:

答案 0 :(得分:6)

问题归结为T::class为您提供了KClass<T>,而t::class为您提供了KClass<out T>。请考虑以下事项:

class Foo {
    val foo = 2
    val bar = 3
}

fun f() {
    val any: Any = Foo()
    any::class.memberProperties.forEach {
        println(it.get(2)) // Oops
    }
}

这实际上会尝试访问2.foo2.bar,但不允许这样做,因为get在谨慎方面犯了错误,而不是允许Any类型的参数1}}。但是,看起来t.javaClass.kotlin会产生KClass<T>。如上所述滥用它会导致IllegalArgumentException

您可以通过提供KClass将为该类型而不是其他内容的编译时保证来为编译器提供更多帮助:

private inline fun <reified A : Annotation, reified T : Any> foo(target: T) {
    T::class.memberProperties
            .filter { it.annotations.any { annotation -> annotation is A } }
            .forEach {
                println(it.get(target))
            }
}

很遗憾,我不知道从A推断T时是否可以指定target。我还没有像foo<Attr, Bar>(bar)那样找到一种方法。

或者,你可以浏览javaClass,虽然我打赌它的便携性不足:

private inline fun <reified A : Annotation> foo(target: Any) {
    target.javaClass.kotlin.memberProperties
            .filter { it.annotations.any { annotation -> annotation is A } }
            .forEach {
                println(it.get(target))
            }
}

我们知道这不会遇到上述问题,因为我们在两种情况下都传递相同的对象。这对于来电者来说也看起来更好,这可能值得携带,如果有的话。(/ p>