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

dan*_*iol 2 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?
                 }
}
Run Code Online (Sandbox Code Playgroud)

我想使用it.get(...)但是get期望一个Nothingas参数.同样如此getter.调用it.call(target)确实有效但看起来不对,因为有一个get我不知道如何调用的实际值.

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

chr*_*ris 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
    }
}
Run Code Online (Sandbox Code Playgroud)

这将从根本上试图访问2.foo2.bar,但它不允许的,因为get谨慎的一侧允许犯错类型的参数,而不是Any.然而,它似乎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))
            }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我不知道这是否是可能的指定A而推导Ttarget.我还没有找到一种方式来称呼它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))
            }
}
Run Code Online (Sandbox Code Playgroud)

我们知道这不会遇到上述问题,因为我们在两种情况下都传递相同的对象.这在呼叫者方面也看起来更好,假设有一个,可能值得携带.