tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

[JavaSE] Introspection

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"

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
Class classe = Class.forName("paquetage.nom_de_la_classe");

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

System.out.println((objet instanceof Enfant)?"vrai":"faux");
Object o = classe.getConstructor(String.class).newInstance();

Introspection sur une classe existante [OPTIONNEL]

Choisissez un classe que vous connaissez, par exemple java.lang.Double :

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

  1. Créer une interface Algorithme qui dispose d’une méthode appliquer(double d[]).
  2. Écrire une classe Afficher qui implémente l’interface définie précédemment et dont la méthode appliquer() affiche le tableau passé en paramètre. Nous sommes en train d’écrire un foncteur : autrement dit une classe qui encapsule une fonction.
  3. Écrire un programme qui déclare un tableau de double et initialise ce tableau avec des valeurs pseudo aléatoires. L’utilisateur pourra spécifier le nom de toute classe implémentant l’interface Algorithme (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é.
  4. Créer une classe Trier qui implémente l’interface Algorithme. La méthode appliquer() réalisera le tri rapide du tableau. Cette fonction existe, je vous laisse googler pour la trouver.
  5. 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 !!!

Greffon générique - à faire lorsque les génériques auront été vus

Dans l’exemple précédent, le programme principal était paramétré par un foncteur et le type de données était fixé. Nous allons maintenant proposer une version où mêmes les données sont paramétrées.

  1. Proposer une interface paramétrée sur le type de données. On proposera outre la méthode appliquer(), une méthode pour charger() les données (sorte de setter)
  2. Créer le type de données qui permet de faire comme en première partie (i.e dans la partie non générique).
  3. Écrire les foncteurs Afficher et Trier.
  4. Adapter le programme principal.

Il est tout à fait possible de fixer des contraintes sur la classe de données générique : pour trier des éléments, il faut que ceux-ci soient Comparable.