Date de première publication : 2010/05/10
Derrière le terme anglais de reflection se cachent en Français deux notions : l’introspection et l’intercession. Un programme JAVA s’exécute dans une machine virtuelle : il est possible de consulter des informations sur les classes (attributs, méthodes et constructeurs) à l’exécution : cela s’appelle l’introspection. Cette notion se rapproche de RTTI (Runtime Type Information) du C++, vu en ZZ3.
Si le gestionnaire de sécurité le permet, on peut également "modifier" les classes dynamiquement, c’est l’intercession.
Pour quasiment tous les types d’éléments que l’on peut manipuler, il y a une classe correspondante :
Class, AccessibleObject, Constructor, Method et Field... (On a une représentation objet de l'objet :-))
On peut choisir de lister soit tous les éléments publics, soit tous les éléments (declared), [mode ="python"] ce qui n'est pas respectueux de l'encapsulation [fin mode]
Découverte de l'introspection
Utilisation de classes "maison"
- Créer une classe
Personneavec un attributnomde type chaîne de caractères et deux constructeurs : un sans argument (avec initialisation par défaut) et l'autre qui initialise le nom - Créer une classe
Enfantqui dérive dePersonne. - Le constructeur de
Personnedoit afficher le nom de la classe de l’objet créé. - Instancier un objet de chaque classe et vérifier l'affichage sur la console
Personne p = new Personne();
On peut le faire car tout objet connaît sa classe grâce à la méthode getClass().
Pour les types primitifs et void, il suffit d’utiliser le "champ" class pour avoir la même information.
objet.getClass()
UneClasse.class
int.class
- Charger la classe
Personneen mémoire grâce à la méthodeforName(). D'habitude, les classes que l'on utilise sont chargées automatiquement par la machine virtuelle à partir du moment où la machine estime qu'elle en a besoin à partir du code écrit et compilé par nos soins. L'intérêt de la méthodeforName()est de pouvoir charger une classe avec un nom déterminé à l'exécution (et pas à la compilation)
Class classe = Class.forName("paquetage.nom_de_la_classe");
- Créer une instance de
Personnegrâce ànewInstance(). Vérifier que le constructeur sans argument est appelé.
Object o = classe.newInstance(); // Java 8
Object o = classe.getDeclaredConstructor().newInstance(); // Java 9 et +
Si vous avez une erreur sur l'exception InvocationTargetException, avez-vous pensé ...
à importer le bon package
- Vérifier lors de l’exécution que cette instance n’est pas une instance de la classe
Enfant.
System.out.println((objet instanceof Enfant)?"vrai":"faux");
- Charger la classe
Enfant. Créer un objet de cette classe et refaire les étapes précédentes. - Afficher le nom de la classe mère de l’objet de classe
Enfant - Lister par introspection les constructeurs de la classe
Personne. - Pouvez-vous instancier un objet de la classe
PersonneavecnewInstance()et des arguments ?
Object o = classe.getConstructor(String.class).newInstance();
Introspection sur une classe existante [OPTIONNEL]
Choisissez un classe que vous connaissez, par exemple java.lang.Double :
- Lister les interfaces que la classe implémente,
- Lister les méthodes de la classe,
- Lister les attributs/champs de la classe. Vérifier la visibilité des éléments.
Application : programmation d'un système de greffons
Nous allons mettre en œuvre le principe de l’introspection et surtout du chargement dynamique de
classe pour produire une toute petite application dont le code n’est pas entièrement connu à la
compilation. Un utilisateur pourra fournir des algorithmes à appliquer sur un tableau de doubles lors
de l’exécution du programme.
Greffon non générique
- Créer une interface
Algorithmequi dispose d’une méthodeappliquer(double d[]). - Écrire une classe
Afficherqui implémente l’interface définie précédemment et dont la méthodeappliquer()affiche le tableau passé en paramètre. Nous sommes en train d’écrire un foncteur : autrement dit une classe qui encapsule une fonction. - Écrire un programme qui déclare un tableau de
doubleet initialise ce tableau avec des valeurs pseudo aléatoires. L’utilisateur pourra spécifier le nom de toute classe implémentant l’interfaceAlgorithme(saisie ou paramètre à l'exécution). Si la classe respecte bien l’interface, alors l’algorithme sera appliqué sinon le tableau sera seulement affiché. - Créer une classe
Trierqui implémente l’interfaceAlgorithme. La méthodeappliquer()réalisera le tri rapide du tableau. Cette fonction existe, je vous laisse googler pour la trouver. - Tester votre programme principal avec cette nouvelle classe.
String nom_algo = "Trier";
// le nom peut être lu au clavier ou donné en paramètre à l'exécution
// charger la classe
// verifier son type
// exécuter
Si vous voulez lire des données au clavier, vous pouvez utiliser une Console dans un programme standalone
mais pas sous Eclipse. La console n'est pas disponible. Le plus simple est de passer le nom de la classe en paramètre à la méthode de classe main()
Dans un programme Java classique, le fait d'utiliser une classe dans le code fait qu'elle est compilée si ce n'est pas déjà fait. Les classes qui implémentent l'interface Algorithme ne sont pas compilées automatiquement car elles ne sont pas liées au code qui s'exécute. Compilez-les avant de les donner à votre programme principal !!!


