Accueil / Articles PiApplications. / La plate-forme Java / Exemples de codes source Java

Création d'un objet par réflexion.

Le mécanisme de la réflexion est un mécanisme puissant qui permet de rendre un code le plus générique possible. Grâce à lui, il est possible d'instancier des objets sans connaître à l'avance leur classe ou d'invoquer leurs méthodes sans connaître leurs classes. Nous allons voir ici comment créer un objet par réflexion.

Au départ, nous ne connaissons de la classe que son nom complet (nom précédé de son package). Bien entendu, le chargeur de classe doit être en mesure de la charger même si l'on ne sait pas à l'avance quelle sera la classe de l'objet à créer. En effet, une classe est un "patron", c'est à dire un descripteur qui précise le prototype des attributs, des constructeurs et des méthodes. De façon régulière, lorsque l'on demande à instancier un objet via l'opérateur new, la JVM charge ce descripteur et en déduit les allocations afférentes. Le mécanisme de réflexion permet de faire la même chose sans l'opérateur new.

Le descripteur d'une classe est accessible via la classe Class. Cette classe possède une méthode statique nommée forName qui instancie un objet correspondant à ce descripteur de classe. Notez que comme la classe Class s'applique à toute classe, la référence retournée est une référence sur un objet de classe Object car c'est la classe ancêtre de toute classe.

public static Object instantiate(String sClassName)
{
  return Class.forName(sClassName).newInstance();
}

Dans la pratique, on peut ensuite utiliser des opérateurs comme instanceof pour déterminer la classe de l'objet et effectuer un éventuel transtypage.

Pour que cette méthode fonctionne, la classe à instancier doit avoir un constructeur public sans paramètre. Il est cependant possible d'instancier une classe possédant un constructeur paramétré (on parle "d'arité" du constructeur). Nous en donnons ici un exemple :

public static Object instantiate(String sClassName, List<String> lstParams)
{
  Class<?> cls = Class.forName(sClassName);
  // recherche d'un constructeur approprié aux arguments transmis
  for (Constructor<?> cns : cls.getConstructors())
  {
    Class<?>[] acls = cns.getParameterTypes();
    if (lstParams.size() == acls.length)
    {
      // Conversion des arguments de types chaînes en arguments de type Object
      Object[] aobj = new Object[acls.length];
      for (int k = 0; k < aobj.length; k++)
        aobj[k] = convert(lstParams.get(k));
      return cns.newInstance(aobj);
    }
  }
  throw new IllegalArgumentException("Aucun constructeur de la classe ne répond à la liste des paramètres transmise.");
}

Dans cet exemple nous faisons appel à une hypothétique méthode convert pour convertir une liste de chaînes en une liste d'objets. Il aurait tout autant été possible de transmettre directement cette liste de paramètres directement. Tout dépend si le contexte le permet ou non.

La réflexion a toutefois un coût : elle est assez nettement moins performante que l'opérateur new qui lui même est déjà assez "lent" en raison de la mise à jour des graphes d'objets lorsqu'il est invoqué. Il faut donc éviter d'employer ce mécanisme sur un trop grand nombre d'objets consécutivement (grande boucle par exemple).

(c) PiApplications 2016