extrait de http://ricky81.developpez.com/tutoriel/java/api/reflection/ riky81Présentation du paquetage java.lang.reflectLe paquetage java.lang.reflect contient les éléments indispensables pour utiliser l'introspection. Mais commençons par évoquer la classe java.lang.Class qui est la classe centrale sur laquelle repose la réflexivité. La classe ClassEn effet, vous avez sans doute déjà rencontré cette classe à l'occasion d'un :
qui permet de récupérer un objet de type Class correspondant au nom passé en paramètre (par exemple à l'occasion de l'utilisation de JDBC). En fait, les instances de la classe Class peuvent être des classes ou des interfaces. Cette classe est indispensable pour pouvoir manipuler des méta données. Nous verrons que cette classe sera présente dans chaque exemple que nous rencontrerons, dont en voici un premier :
Dans cet exemple, la méthode getNom va renvoyer le nom de la classe de l'objet passé en paramètre. Cette classe dispose des méthodes permettant d'extraire les informations sur une classe quelconque. En voici quelques unes :
La classe FieldVenons en à notre paquetage java.lang.reflect. Nous avons vu qu'il était possible de récupérer des méta données sur les champs à partir d'une classe en appelant par exemple la méthode getField. Partant d'un tel objet, nous pouvons consulter les informations à l'aide de méthodes de classe :
La classe ModifierNous avons vu précédemment qu'il était possible de connaître la visibilité d'un champ par l'intermédiaire de la méthode getModifier(). Il peut être surprenant de constater qu'il existe une classe Modifier alors que la méthode précédente renvoie un entier. Ce qu'il faut bien avoir à l'esprit pour comprendre ceci, c'est qu'il y a plusieurs informations à l'intérieur de cet entier, et il est nécessaire de faire appel à des méthodes de classe pour les distiller (néanmoins on conviendra qu'il pourrait être plus logique d'adapter la classe Modifier pour pouvoir faire renvoyer un objet plutôt qu'un entier). Voici quelques unes des méthodes utiles :
Ainsi, le code suivant permet de déterminer si le champ 'f' possède une visibilité publique :
Néanmoins, il existe une méthode assez pratique, à savoir String static toString(int mod), qui permet de récupérer une description classique complète des informations. La classe MethodPourquoi ne pas avoir géré les informations disponibles dans la classe Modifier directement dans la classe Field me direz vous ? De telles informations sont justement également disponibles pour les méthodes et il est donc plus censé de les factoriser dans une classe spécifique. Pour en revenir à nos méthodes, nous avons vu qu'il était possible de récupérer un objet décrivant une méthode, et nous allons voir ce qu'il est possible d'en extraire comme informations :
La classe ConstructorEnfin, dans le même esprit que pour la classe Method, on retrouve un classe Constructor qui permet de récupérer des informations sur un constructeur.
Cas particulier des types primitifsLe lecteur averti aura sans doute une question concernant la compatibilité entre la classe Class et les types primitifs de Java comme int, long, char, ... Ces types n'étant pas des objets, comment se passe la compatibilité avec les méthodes que nous avons vues précédemment et qui renvoient des objets de type Class ? Et bien ce sont les types objets enveloppe correspondants (Integer, Long, Char, ...) qui seront renvoyés. Même remarque lorsque nous tenterons d'appeler une méthode dynamiquement à l'aide de la réflexivité : il faudra passer comme paramètre un objet et non le type primitif qui est utilisé au niveau de la méthode, mais également caster le résultat renvoyé dans le type objet enveloppe correspondant (cela ne devrait plus être nécessaire avec Java 1.5). Néanmoins, se pose alors une autre question. A savoir : comment distinguer 2 méthodes entièrement identiques ne différant que par un type lequel est pour l'une un type primitif, et pour l'autre le type objet enveloppe correspondant ? Prenons l'exemple du couple Integer/int : pour le premier ce sera Integer.class et pour le second Integer.TYPE. Ainsi, dans l'exemple suivant :
m1 correspond ici à la méthode somme(Integer, Integer) alors que m2 correspond à somme(int, int). Utilisation de la réflexivité - Exemples d'appels dynamiquesVoyons à présent quelques exemples d'utilisation pratique de la réflexivité. Nous allons dans un premier temps essayer de modifier le contenu du champ d'un objet de façon dynamique en prenant pour paramètres un objet, le nom du champ qu'on souhaite modifier, et la nouvelle valeur. Puis, nous travaillerons au niveau des méthodes pour vous montrer ce qu'il est possible de réaliser. Edition d'un champImaginez le problème suivant : vous souhaitez pouvoir modifier la valeur du champ d'un objet de façon dynamique. Vous allez donc passer en paramètre le nom de ce champ sous la forme d'un String, mais vous ne pourrez pas agir sur ce champ avec les moyens classiques. Considérons donc une telle méthode :
Il n'est bien entendu pas possible d'accéder au contenu du champ comme en programmation statique par :
Nous sommes donc obligés de faire appel à la réflexivité. Voici le code que nous allons par la suite commenter :
Nous récupérons donc un objet de type Field correspondant au champ concerné par la modification, puis nous faisons appel à la méthode set sur ce champ qui permet de modifier le contenu du champ d'un objet (ici o) en lui attribuant la valeur passée en second paramètre. La méthode set est la méthode la plus générale pour faire une affectation sur un objet, mais il existe des méthodes plus restreintes tel que setDouble(Object obj, double d)ou setBoolean(Object obj, boolean z). Il y a bien entendu la possibilité d'utiliser des méthodes de consultation, ce qui permet d'écrire une méthode générique d'affichage du contenu d'un champ d'un objet :
Appel d'une méthodeAprès avoir vu qu'il était possible d'agir sur les champs d'un objet à l'aide de la réflexivité, nous allons maintenant nous intéresser aux méthodes. Nous allons ainsi pouvoir lancer une méthode sur un objet de façon entièrement dynamique, en gérant bien entendu le passage des paramètres souhaités à la méthode. Nous avons vu une certaine catégorie de méthodes pour la classe Method, à savoir ce qui concerne l'interrogation des méta données d'une méthode. Comme nous avons vu comment récupérer la valeur d'un champ et la modifier, nous allons voir maintenant la méthode à utiliser pour lancer dynamiquement une méthode sur un objet ou une classe. La méthode en question est invoke dont voici la déclaration :
Dans le même esprit des exemples précédents, nous pouvons alors déclarer la méthode suivante :
Cette méthode permet donc de lancer une méthode sur un objet, et éventuellement de récupérer un résultat, et ceci en récupérant la liste des paramètres et le nom de la méthode. Attention cependant : comme cela a été dit précédemment, la recherche de la méthode par getMethod va examiner les types des paramètres et il est donc indispensable de ne pas se tromper au niveau des paramètres. De plus, cela pose quelques problèmes pour les méthodes génériques ayant pour paramètre le type Objectmais qu'on désire appeler avec un paramètre d'un type dérivé. En effet, getMethod cherchera la méthode dont le paramètre a pour type le type dérivé en question, et comme il ne le trouvera pas il renverra une exception. Je vous laisse chercher une solution à ce problème ...
Réalisation d'un inspecteur de classeVoici un exemple très basique de ce qu'on peut faire pour consulter les informations à visibilité publique d'une classe en affichant les informations avec le classique System.out.
|