La réflexion en Java
Description
Tutoriel sur la réflexion en Java. On verra comment récupérer les constructeurs, méthodes et champs d'une classe en utilisant la réflexion. On verra également comment éxecuter une méthode, constructeur quelconque d'une classe.
Introduction
Bonjour, ça fait très longtemps que je n'ai pas posté sur ce site par manque de temps depuis le début de mon activité professionnelle, c'est qu'avant, quand j'étais étudiant, c'était beaucoup plus facile !!!
Pour reprendre en douceur, voici un petit tutoriel sur la réflexion en Java (rien à voir à l'activité de Réfléchir pour ceux qui ont déjà sortis l'aspirine !).
Pour un débutant en Java, cet article ne lui sera pas d'une grande utilité. Bien que simple, la réflexion peut paraître un peu superflue ou inutile mais avec un peu d'expérience dans des projets informatique d'envergure, la réflexion peut rendre bien des services.
Petite note sur le code présenté dans ce tutoriel : le code est prévu pour Java 6. Il est néanmoins très facile de le rendre compatible avec une version inférieure. En cas de difficulté, me poser la question.
Qu'est ce que la Réflexion ?
D'après Wikipédia,
En programmation informatique, la réflexion est la capacité d'un programme à examiner, et éventuellement à modifier, ses structures internes de haut niveau (par exemple ses objets) lors de son exécution.
Pour être plus clair, la réflexion c'est donner la possibilité au programme de pouvoir accéder/modifier la structure d'un objet pendant l'exécution du programme. Par structure d'un objet, on entend tout ce qui est propriété, classe, fonction etc.. En général, dans des langages « classiques » (C ou C++ par exemples), la structure d'un objet est uniquement connue au moment de la compilation, c'est-à-dire qu'il n'est pas possible, à l'exécution, de connaître « ce qu'a dans le ventre » un type d'objet quelconque.
Java implémente la réflexion. Il donne cependant uniquement la possibilité d'accéder aux structures et non pas de les modifier. Ainsi, à l'exécution d'un programme Java, il est tout à fait possible de connaître précisément la structure de n'importe quel objet et de pouvoir interagir avec (c'est à dire par exemple exécuter n'importe qu'elle fonction de l'objet) mais on ne peut pas par exemple, ajouter une fonction ou un champ à l'objet.
La structure d'un objet en Java est caractérisée par une classe qui contient des constructeurs, des méthodes (ou fonctions) et enfin des champs. La réflexion en Java permet d'avoir au moment de l'exécution la définition de n'importe quel objet tel que l'on avait programmé.
Mise en oeuvre
Le package
java.lang.reflect est consacré à la réflexion. Comme vu plus haut, la structure d'un objet Java est caractérisée par une classe qui contient des constructeurs, des méthodes (ou fonctions) et enfin des champs. Les caractéristiques de l'objet sont représentés de manière formelle par une classe du package de réflexion :
Classe du package | Représente |
Class | Une classe. |
Method | Une méthode d'une classe |
Constructor | Constructeur d'une classe |
Field | Un champ d'une classe |
Notons, pour éviter une remarque en commentaire, que
Class ne fait pas parti du package
java.lang.reflect mais de
java.lang, Néanmoins
Class participe bien au mécanisme de la réflexion.
J'ai limité volontairement la liste des classes du package. Le but du présent tutoriel est de donner une première approche de la réflexion. Je laisserai donc le soin au lecteur de s'informer plus en détail s'il a besoin d'approfondir un peu.
Qu'est ce que ces classes nous apprennent sur l'objet ? La réponse est simple : tout !! Par exemple, à l'aide de
Method, on peut connaître le nom de la méthode, les paramètres qu'elle prend et enfin le type de la valeur de retour.
Je vous recommande vivement de faire un tour sur la page suivante, qui est malheureusement pour certains, en anglais :
http://java.sun.com/javase/6/docs/api/java/lang/reflect/package-summary.html
Pour mieux comprendre, prenons la classe suivante :
public class Test1
{
private int unChamp2;
public Test()3
{
}
public int methode(String arg)4
{
}
}
La structure de l'objet Test est donc la suivante :
- 1 : Un Class représentant la classe Test
- 2 : Un Field représentant le champ unChamp
- 3 : Un Constructor représentant le constructeur de la classe
- 4 : Un Method représentant la méthode methode
Pour accéder à toute la structure d'un objet, tout se fait à partir de
Class.
Pour récupérer la classe d'une instance d'un objet, rien de plus simple, un simple
getClass sur l'objet et le tour est joué :
Class classe=obj.getClass();
Il est également possible de récupérer la classe à partir d'un type d'objet. Par exemple, pour
String :
Class classe=String.class;
A partir de là, pour récupérer (des exemples sont développés plus bas) :
- Une méthode, utiliser getMethod()
- Un constructeur, utiliser getConstructor()
- Un champ, utiliser getField()
On a vu plus haut, que la réflexion ne servait pas seulement à accéder à la structure d'un objet mais aussi à interagir avec l'objet. Ainsi, on peut appeler une méthode quelconque, construire une instance à partir d'un constructeur ou affecter une valeur à un champ. Vous l'aurez compris, ces opérations sont réalisables à partir des classes
Method,
Constructor et
Field.
Pour créer une nouvelle instance d'un objet, on utilise la fonction newInstance de
Constructor. Cette fonction renvoie une nouvelle instance.
Ensuite, pour appeler une méthode, on utilise la fonction invoke de
Method. Cette fonction permet d'exécuter la méthode sur une instance d'un objet.
Enfin, pour affecter une valeur à un champ, on utilise la fonction
set de Field.
Nous allons mettre en pratique les notions vues jusqu'ici à travers plusieurs exemples pour mieux comprendre.
Exemples d'accès à la structure d'un objet
A travers les exemples ci-dessous, on va apprendre à utiliser la réflexion pour accéder à la structure d'un objet quelconque. Vous allez voir que c'est très simple.
Récupérer la classe d'un objet
Soit obj un objet quelconque, le code suivant donne sa Class. N'oublions pas que récupérer la classe est le point de départ pour avoir toutes les informations d'un objet
Class classe=obj.getClass();
Il est également possible de récupérer la classe d'un type par exemple de String :
Class classe=String.class ;
Récupérer une méthode
Du moment que l'on a récupéré la classe, on peut accéder aux méthodes de la classe.
Toutes les méthodes
Le code suivant donne toutes les méthodes de la classe dans un tableau.
import java.lang.reflect.Method; //ligne à rajouter en début de classe
Method methods * =classe.getMethods();
Récupérer une méthode particulière
Une méthode est identifiée par sa signature c'est à dire son nom et la liste des types de paramètres. Notez que la réflexion Java permet de remonter uniquement les méthodes en visibilité public (contrairement au CSharp).
Imaginons que l'on veuille la fonction
concat qui prend comme paramètre une chaîne de caractères (String). Le code est le suivant :
Method concat=classe.getMethod("concat", String.class);
Le premier paramètre de
getMethod est le nom de la méthode recherchée. Attention le nom est sensible aux majuscules/minuscules. Les paramètres qui suivent sont les types des paramètres de la fonction. Ici, on veut la fonction
concat avec exactement un paramètre de type String.
Si vous essayez ce code, votre compilateur risque de râler sur les exceptions non gérées.
Exceptions levées :
Quand on utilise
getMethod, deux exceptions sont susceptibles d'êtres levées. Ces exceptions doivent obligatoirement être gérées pour que le code plus haut soit correct.
- NoSuchMethodException : quand la méthode demandée n'existe pas
- SecurityException : quand votre code n'est pas autorisé à récupérer la méthode
Ainsi, le code plus haut complet pour qu'il compile est le suivant :
try {
Method concat=classe.getMethod("concat", String.class);
} catch (SecurityException e) {
//prb de sécurité
e.printStackTrace();
} catch (NoSuchMethodException e) {
//la méthode n'existe pas
e.printStackTrace();
}
Récupérer un constructeur
Un constructeur, contrairement à une méthode, n'est pas identifié par son nom (puisque par définition, un constructeur porte toujours le nom de la classe) mais uniquement par le type de ses paramètres. Comme pour les méthodes, notez que la réflexion Java permet de remonter uniquement les constructeurs en visibilité public.
Le principe reste le même que pour les méthodes sauf qu'au lieu d'utiliser
getMethod, on utilise
getConstructor.
Tous les constructeurs
import java.lang.reflect.Constructor; //à rajouter au début du fichier
Constructor * constructeurs=classe.getConstructors();
Récupérer un constructeur en particulier
try {
Constructor constructeur=classe.getConstructor(StringBuilder.class);
} catch (SecurityException e) {
//prb de sécurité
e.printStackTrace();
} catch (NoSuchMethodException e) {
//le constructeur n'existe pas
e.printStackTrace();
}
Notez que les exceptions levées sont les mêmes que pour
getMethod.
Récupérer un champ
Pour récupérer un champ, on utilise
getField. Il faut encore noter que l'on récupère uniquement les champs en visibilité public.
Tous les champs
import java.lang.reflect.Field;
Field * champs=classe.getFields();
Un champ en particulier
Un champ est identifié uniquement par son nom. Supposons que l'on veuille le champ name de l'objet :
try {
Field champ=classe.getField("name");
} catch (SecurityException e) {
//prb de sécurité
e.printStackTrace();
} catch (NoSuchFieldException e) {
//le champ n'existe pas
e.printStackTrace();
}
Quand le champ n'existe pas, c'est l'exception
NoSuchFieldException qui est levée.
Exemples d'interactions avec un objet
Avec les exemples ci-dessus, vous voyez que je ne vous avez pas menti : la réflexion est très simple.
C'est bien beau de récupérer les méthodes et ses informations, mais ça serait encore plus beau si on pouvait interagir avec n'importe quel objet ! Par exemple, appeler une méthode quelconque ou construire une instance quelconque.
Une fois qu'on a les
Method,
Constructor, et
Field, il est encore plus simple d'effectuer des opérations sur un objet.
Construire une instance à partir d'un Constructor
A partir de
Constructor récupéré sur un type, il est très facile de construire une instance. On utilise pour cela la fonction
newInstance. Cette fonction prend la liste des paramètres du constructeur.
En reprenant le constructeur récupéré plus haut, ce constructeur prend en paramètre un
StringBuilder, il faut donc impérativement lui en passer un sous peine d'exception :
try {
Object instance=constructeur.newInstance(new StringBuilder());
} catch (IllegalArgumentException e) {
//le nombre d'arguments n'est pas bon
e.printStackTrace();
} catch (InstantiationException e) {
//la classe est abstraite (instance d'une classe abstraite impossible)
e.printStackTrace();
} catch (IllegalAccessException e) {
//le constructeur n'est pas accessible
e.printStackTrace();
} catch (InvocationTargetException e) {
//le constructeur a levé une exception
e.printStackTrace();
}
La fonction
newInstance va renvoyer la nouvelle instance du type.
Les exceptions qui peuvent être levées sont les suivantes :
- IllegalArgumentException : le nombre d'arguments n'est pas bon ou ne sont pas du bon type
- InstantiationException : lorsqu'on essaye d'instancier une classe abstraite (impossible en Java)
- IllegalAccessException : le constructeur n'est pas accessible (est en visibilité privée par exemple)
- InvocationTargetException : le constructeur a levé une exception
Appel à une méthode
A partir de l'objet
Method, on peut exécuter la méthode sur une instance de l'objet. On utilise la fonction
invoke qui prend en paramètre une instance d'un objet sur laquelle exécuter la méthode et la liste des paramètres de la méthode.
En reprenant la méthode récupérée dans l'exemple plus haut.
try {
Object objet= " bonjour");
Object resultat=concat.invoke(objet, " tout le monde");
} catch (IllegalArgumentException e) {
//les arguments passés à la méthode ne sont pas bon
e.printStackTrace();
} catch (IllegalAccessException e) {
//fonction pas accessible
e.printStackTrace();
} catch (InvocationTargetException e) {
//la méthode a levée une exception
e.printStackTrace();
}
Si la méthode exécutée doit renvoyer une valeur, cette valeur est renvoyée en retour de l'appel à
invoke.
Les exceptions qui peuvent être levées sont les suivantes :
- IllegalArgumentException : le nombre d'arguments n'est pas bon ou ne sont pas du bon type
- IllegalAccessException : la méthode n'est pas accessible (est privée par exemple)
- InvocationTargetException : la méthode a levé une exception
Affectation d'une valeur à un champ
Pour affecter une valeur à un champ d'une instance d'un objet, on utilise la méthode set de Field du champ à modifier.
try {
champ.set(objet, "tuto");
} catch (IllegalArgumentException e) {
//le parametre passé n'est pas valide (pas du bon type par exemple)
e.printStackTrace();
} catch (IllegalAccessException e) {
//fonction non accessible
e.printStackTrace();
}
Les exceptions qui peuvent être levées sont les suivantes :
- IllegalArgumentException : la valeur du champ à définir n'est pas du bon type
- IllegalAccessException : le champ n'est pas accessible (est privé par exemple)
Lecture d'une valeur d'un champ
De la même manière, on peut lire la valeur d'un champ en utilisant la fonction get de Field.
try {
Object lu=champ.get(objet);
} catch (IllegalArgumentException e) {
//le parametre passé n'est pas valide (pas du bon type par exemple)
e.printStackTrace();
} catch (IllegalAccessException e) {
//fonction non accessible
e.printStackTrace();
}
Exemple complet
Voici un exemple complet de réflexion. Etudiez ce code, il n'est vraiment pas compliqué, il regroupe toutes les notions vus dans ce tutoriel.
On défini 3 classes : A, B et C.
On va tirer au hasard une des trois classes. Sur cette classe, on affichera la liste des méthodes, des champs et des constructeurs en utilisant la réflexion.
Ensuite, toujours en utilisant la réflexion, une instance de la classe sera créée en utilisant un constructeur ayant un argument de type String s'il existe pour la classe.
On appellera ensuite la méthode, si elle existe, tutoriel sur l'instance qui vient d'être créé. Cette méthode doit prendre deux arguments de type String.
Pour finir, on affichera également les valeurs des champs de l'objet.
Voici le code complet, il doit être copié collé dans votre éditeur Java favori. L'exécution de l'exemple s'effectue en lançant simplement la classe
ExempleComplet (contient une méthode main).
Fichier A.java
public class A
{
//**** Methodes ****
private int champ1=45;
public String champ2="valeur";
protected double champ3=2.0;
public int valA=4;
//*** Constructeurs ****
public A()
{
}
public A(double val,String val2)
{
}
private A(int val,String val2)
{
}
public A(String a)
{
}
//*** Méthodes ***
public int compute()
{
System.out.println("Compute éxecuté !!!");
return 0;
}
public void add(int a,int b)
{
System.out.println("add éxecuté !!!");
}
private void div(int a,int b)
{
System.out.println("div éxecuté !!!");
}
public String methodeA()
{
return "methodeA";
}
}
Fichier B.java
public class B
{
//**** Methodes ****
private String joueur="toto";
public int score=454676;
//*** Constructeurs ****
public B()
{
}
public B(String joueur)
{
}
//*** Méthodes ***
public int tutoriel(String a,String b)
{
System.out.println("tutoriel de B éxecuté !!!");
return 0;
}
private int test()
{
System.out.println("test éxecuté !!!");
return 1;
}
public String methodeB()
{
return "methodeA";
}
}
Fichier C.java
public class C
{
//**** Methodes ****
private String unChamp="champ";
public int unChamp2=244;
//*** Constructeurs ****
public C()
{
}
public C(String val)
{
}
//*** Méthodes ***
public int tutoriel(String a,String b)
{
System.out.println("tutoriel de C éxecuté !!!");
return 20;
}
public String toString()
{
System.out.println("toString éxecuté !!!");
return "Classe C";
}
public String methodeC()
{
return "methodeA";
}
}
Fichier ExempleComplet.java
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ExempleComplet
{
/**
* Point d'entrée dans le prg
* @param args
*/
public static void main(String * args)
{
//Tableau des classes
Class classes * ={A.class,B.class,C.class};
//prenons une classe au hasard dans le tableau
Class classe=classes[(int) (Math.random()*Integer.MAX_VALUE) % classes.length * ;
//affiche a l'écran la classe choisie
System.out.println("Classe choisie : "+classe.getName());
//affichons la liste des constructeurs de cette classe
System.out.println("*** Liste des constructeurs de la classe ****");
Constructor constructeurs * =classe.getConstructors();
for(Constructor constructeur : constructeurs)
{
System.out.println("Constructeur : "+constructeur.getName()+", Nb d'arguments : "+constructeur.getParameterTypes().length);
}
//affichons la liste des méthodes de cette classe
System.out.println("*** Liste des méthodes de la classe ****");
Method methodes * =classe.getMethods();
for(Method methode : methodes)
{
System.out.println("Méthode : "+methode.getName()+", Nb d'arguments : "+methode.getParameterTypes().length+", Type en retour : "+(methode.getReturnType()==null ? null : methode.getReturnType().getName()));
}
//affichons la liste des champs
System.out.println("*** Liste des champs de la classe ****");
Field champs * =classe.getFields();
for(Field champ : champs)
{
System.out.println("Champ : "+champ.getName()+", Type : "+champ.getType().getName());
}
//Récupere le constructeur ayant un parametre String
System.out.println("*** Recupération du constructeur avec un parametre String ****");
Constructor monConstructeur=null;
try
{
monConstructeur=classe.getConstructor(String.class);
} catch (SecurityException e)
{
System.out.println("Erreur de sécurité sur le constructeur");
return;
} catch (NoSuchMethodException e)
{
System.out.println("Le constructeur n'existe pas !");
return;
}
//construction d'une instance de l'objet
System.out.println("*** Création d'une instance à partir de ce constructeur ****");
Object instance=null;
try
{
instance=monConstructeur.newInstance("tuto");
} catch (IllegalArgumentException e)
{
System.out.println("Erreur Argument invalide");
return;
} catch (InstantiationException e)
{
System.out.println("Erreur impossible d'instancier une classe abstraite");
return;
} catch (IllegalAccessException e)
{
System.out.println("Erreur constructeur par accessible");
return;
} catch (InvocationTargetException e)
{
System.out.println("Erreur exception levée par le constructeur");
return;
}
//Récupération de la méthode tutoriel avec deux arguments String
System.out.println("*** Récupération de la méthode tutoriel ****");
Method tutoriel=null;
try
{
tutoriel=classe.getMethod("tutoriel", String.class,String.class);
} catch (SecurityException e)
{
System.out.println("Erreur de sécurité sur le constructeur");
return;
} catch (NoSuchMethodException e)
{
System.out.println("Erreur la méthode n'existe pas");
return;
}
//Execution de la méthode tutoriel
System.out.println("*** Execution de la méthode tutoriel ****");
try
{
Object retour=tutoriel.invoke(instance, "un","deux");
System.out.println("Valeur en retour="+(retour == null ? null : retour.toString()));
} catch (IllegalArgumentException e)
{
System.out.println("Erreur argument invalide");
return;
} catch (IllegalAccessException e)
{
System.out.println("Erreur la méthode n'est pas accessible");
return;
} catch (InvocationTargetException e)
{
System.out.println("Erreur la méthode a provoquée une exception");
return;
}
//Affichage des valeurs des champs
System.out.println("*** Liste des valeurs des champs ****");
for(Field champ : champs)
{
try
{
Object valeur=champ.get(instance);
System.out.println("Valeur du champ "+champ.getName()+" : "+(valeur == null ? null : valeur.toString()));
} catch (IllegalArgumentException e)
{
System.out.println("Erreur sur le champ "+champ.getName()+", argument non valide");
} catch (IllegalAccessException e)
{
System.out.println("Erreur sur le champ "+champ.getName()+" n'est pas accessible");
}
}
}
}
Résultats d'exécutions
Quand la classe A est choisie :
-------------------------
Classe choisie : A
*** Liste des constructeurs de la classe ****
Constructeur : A, Nb d'arguments : 1
Constructeur : A, Nb d'arguments : 2
Constructeur : A, Nb d'arguments : 0
*** Liste des méthodes de la classe ****
Méthode : compute, Nb d'arguments : 0, Type en retour : int
Méthode : methodeA, Nb d'arguments : 0, Type en retour : java.lang.String
Méthode : add, Nb d'arguments : 2, Type en retour : void
Méthode : wait, Nb d'arguments : 0, Type en retour : void
Méthode : wait, Nb d'arguments : 2, Type en retour : void
Méthode : wait, Nb d'arguments : 1, Type en retour : void
Méthode : hashCode, Nb d'arguments : 0, Type en retour : int
Méthode : getClass, Nb d'arguments : 0, Type en retour : java.lang.Class
Méthode : equals, Nb d'arguments : 1, Type en retour : boolean
Méthode : toString, Nb d'arguments : 0, Type en retour : java.lang.String
Méthode : notify, Nb d'arguments : 0, Type en retour : void
Méthode : notifyAll, Nb d'arguments : 0, Type en retour : void
*** Liste des champs de la classe ****
Champ : champ2, Type : java.lang.String
Champ : valA, Type : int
*** Recupération du constructeur avec un parametre String ****
*** Création d'une instance à partir de ce constructeur ****
*** Récupération de la méthode tutoriel ****
Erreur la méthode n'existe pas
-------------------------
Si la liste des méthodes affichée vous parait suspecte, n'oubliez pas qu'en Java tout objet hérite de Object donc aussi de ses méthodes ! (méthodes wait, hashCode, getClass, equals, toString, notify et notifyAll).
Quand la classe B est choisie :
-------------------------
Classe choisie : B
*** Liste des constructeurs de la classe ****
Constructeur : B, Nb d'arguments : 0
Constructeur : B, Nb d'arguments : 1
*** Liste des méthodes de la classe ****
Méthode : tutoriel, Nb d'arguments : 2, Type en retour : int
Méthode : methodeB, Nb d'arguments : 0, Type en retour : java.lang.String
Méthode : wait, Nb d'arguments : 0, Type en retour : void
Méthode : wait, Nb d'arguments : 2, Type en retour : void
Méthode : wait, Nb d'arguments : 1, Type en retour : void
Méthode : hashCode, Nb d'arguments : 0, Type en retour : int
Méthode : getClass, Nb d'arguments : 0, Type en retour : java.lang.Class
Méthode : equals, Nb d'arguments : 1, Type en retour : boolean
Méthode : toString, Nb d'arguments : 0, Type en retour : java.lang.String
Méthode : notify, Nb d'arguments : 0, Type en retour : void
Méthode : notifyAll, Nb d'arguments : 0, Type en retour : void
*** Liste des champs de la classe ****
Champ : score, Type : int
*** Recupération du constructeur avec un parametre String ****
*** Création d'une instance à partir de ce constructeur ****
*** Récupération de la méthode tutoriel ****
*** Execution de la méthode tutoriel ****
tutoriel de B éxecuté !!!
Valeur en retour=0
*** Liste des valeurs des champs ****
Valeur du champ score : 454676
-------------------------
Quand la classe C est choisie :
-------------------------
Classe choisie : C
*** Liste des constructeurs de la classe ****
Constructeur : C, Nb d'arguments : 0
Constructeur : C, Nb d'arguments : 1
*** Liste des méthodes de la classe ****
Méthode : tutoriel, Nb d'arguments : 2, Type en retour : int
Méthode : methodeC, Nb d'arguments : 0, Type en retour : java.lang.String
Méthode : toString, Nb d'arguments : 0, Type en retour : java.lang.String
Méthode : wait, Nb d'arguments : 0, Type en retour : void
Méthode : wait, Nb d'arguments : 2, Type en retour : void
Méthode : wait, Nb d'arguments : 1, Type en retour : void
Méthode : hashCode, Nb d'arguments : 0, Type en retour : int
Méthode : getClass, Nb d'arguments : 0, Type en retour : java.lang.Class
Méthode : equals, Nb d'arguments : 1, Type en retour : boolean
Méthode : notify, Nb d'arguments : 0, Type en retour : void
Méthode : notifyAll, Nb d'arguments : 0, Type en retour : void
*** Liste des champs de la classe ****
Champ : unChamp2, Type : int
*** Recupération du constructeur avec un parametre String ****
*** Création d'une instance à partir de ce constructeur ****
*** Récupération de la méthode tutoriel ****
*** Execution de la méthode tutoriel ****
tutoriel de C éxecuté !!!
Valeur en retour=20
*** Liste des valeurs des champs ****
Valeur du champ unChamp2 : 244
-------------------------
Conclusion
Pour conclure, comme vous pouvez le constater, la réflexion est très simple et ouvre de nombreuses possibilités. Néanmoins, sous prétexte que vous avez appris un nouveau concept, ce n'est pas une raison pour l'utiliser à toutes les sauces ! Il est parfois bien plus simple d'utiliser des interfaces ou des classes abstraites.
Un des inconvénients de la réflexion, c'est le manque de lisibilité du code. La réflexion est utile pour effectuer des traitements automatiques sur le contenu des objets. Personnellement, dans un projet de site web, pour afficher toutes les informations d'un objet dans une page automatiquement (c'est-à-dire sans se farcir un à un les accès à tous les champs de l'objet), l'utilisation de la réflexion a été pratique et m'a fait gagné un temps fou. En effet, dans chaque page, en parcourant le contenu d'un objet par réflexion, en quelques lignes, j'assure qu'a la moindre modification structurelle de l'objet (par exemple on ajoute un champ dans celui ci) que les répercutions soient faites automatiquement sans effort dans toutes les pages affichant cet objet.
Sur ce, je vous souhaite une bonne Java !! (ah ah c'est fou l'humour que j'ai ! )