Instancier une classe dont on ne connaît que le nom ça paraît inutile et pourtant ça ouvre de nouvelles perspectives ! Ainsi la plupart des logiciels à base de plugins utilisent abondamment cette technique car ils ne connaissent pas à l'avance les classes qu'ils vont manipuler. En ce qui me concerne, j'utilise cette capacité de Java pour le paramétrage de mes composants UIMA. En effet, j'utilise des classes pour calculer des mesures de similarité (c'est plus ou moins mon boulot de thèse) et j'en ai plusieurs, selon mes expériences je suis donc amené à utiliser une métrique plutôt qu'une autre. Cette astuce me permet d'utiliser ces différentes métriques sans développer de nouveaux composants :)

La petite astuce d'introspection java permet donc d'instancier une classe dont on connaît le nom mais à laquelle on n'a pas accès directement. Tout est dans l'utilisation conjointe de la méthode forName de la classe Class et la méthode newInstance accessibles aux classes Class instanciées. Vous suivez ?

En résumé :

  1. On crée une instance de Class à l'aide de Class.forName(), cette instance correspond à la classe que nous souhaitons utiliser (mais c'est encore une classe)
  2. On instancie notre classe tant désirée à l'aide de la méthode newInstance

Ce qui sous forme de code donne :

Class maClasseParticuliere = Class.forName("info.fabienpoulard.ClasseParticuliere");
ClassParticuliere monObjet = (ClassParticuliere) maClassParticuliere.newInstance();

Attention, comme nous l'a confié Uncle Ben (pas celui qui vend du riz, celui qui a élevé Spider Man), with great power comes great responsibility ! Lorsque l'on s'amuse avec ce genre de méthodes, à peu près tout peut mal se passer... et si vous avez des accointances avec [Murphy |http://fr.wikipedia.org/wiki/Loi_de_Murphy|fr] vous savez que ça vous arrivera forcément !

Lorsque l'on utilise cette procédure, plusieurs exceptions peuvent être levées :

  • si la classe n'existe pas ou n'est pas accessible dans le CLASSPATH : ClassNotFoundException
  • si l'instanciation ne se passe pas comme prévu, par exemple car le constructeur nécessite un paramètre que vous ne lui avez pas passé : InstantiationException
  • ou si ça ne veut juste pas fonctionner (pour des raisons de politique de sécurité par exemple) : IllegalAccessException

Il faut donc penser à prendre en compte ces cas et proposer des solutions alternatives ou donner des conseils avisés à vos utilisateurs !

try {
  Class maClasseParticuliere = Class.forName("info.fabienpoulard.ClasseParticuliere");
  ClassParticuliere monObjet = (ClassParticuliere) maClassParticuliere.newInstance();
} catch (ClassNotFoundException e) {
  // Arg la classe n'existe pas ...
  System.out.println("Hey dumbass, check your classpath !");
} catch (InstantiationException e) {
  // reArg j'arrive pas à l'instancier ...
  System.out.println("What kind of class are you playing with, crazy dumbass !");
} catch (IllegalAccessException e) {
  // rereArg ça merde !
  System.out.println("You know what? Fuck you! How about that?");
}

Voilà... des fois que ça serve à d'autres. Au pire ça me servira à moi quand j'aurais oublié que je sais le faire :)