Le problème se résume au fait que T::class
vous donne un KClass<T>
, alors que t::class
vous donne un KClass<out T>
. Considérez ce qui suit:
class Foo {
val foo = 2
val bar = 3
}
fun f() {
val any: Any = Foo()
any::class.memberProperties.forEach {
println(it.get(2)) // Oops
}
}
Cela tenterait essentiellement d'accéder 2.foo
et 2.bar
, mais ce n'est pas autorisé car get
préconisera la prudence au lieu de laisser un paramètre de type Any
. Il semble que t.javaClass.kotlin
produira un KClass<T>
, cependant. L'utilisation incorrecte comme ci-dessus provoque un IllegalArgumentException
.
Vous pouvez donner le compilateur plus d'aide en fournissant une garantie de compilation que le KClass
sera pour le type et rien d'autre:
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))
}
}
Malheureusement, je ne sais pas s'il est possible de spécifier A
tout en déduisant T
de target
. Je n'ai pas trouvé un moyen de l'appeler comme foo<Attr, Bar>(bar)
.
Alternativement, vous pouvez passer par javaClass
, mais je parie qu'il est moins portable:
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))
}
}
Nous savons que cela ne fonctionnera pas dans le problème ci-dessus parce que nous passons le même objet dans les deux cas. Cela semble également plus agréable du côté de l'appelant, ce qui pourrait valoir la peine de portabilité, en supposant qu'il y en a un.
Je pense que si vous faites 'target.javaClass.kotlin.memberProperties' à la place, vous n'avez pas du tout à spécifier' T'. – Todd
@Todd, Intéressant, je me demande pourquoi ils diffèrent. – chris