2017-09-12 5 views
0

Je souhaite utiliser les valeurs de toutes les propriétés comportant des annotations. Pour la plupart, mon code fonctionne, j'ai toutes les propriétés et ne prends que celles qui ont cette annotation.Comment obtenir la valeur d'une propriété avec réflexion

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? 
       } 
} 

Je voulais utiliser it.get(...) mais get un Nothing attend en tant que paramètre. Idem pour getter. L'appel it.call(target) fonctionne mais il semble mal puisqu'il y a un actuall get que je ne sais pas comment invoquer.

Alors, quelle est la bonne façon d'obtenir la valeur des propriétés?

Répondre

3

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.

+0

Je pense que si vous faites 'target.javaClass.kotlin.memberProperties' à la place, vous n'avez pas du tout à spécifier' T'. – Todd

+1

@Todd, Intéressant, je me demande pourquoi ils diffèrent. – chris