21. L'interaction avec le réseau 23. L'appel de méthodes distantes : RMI Imprimer Sommaire Consulter avec table des matières Développons en Java   v 1.60  
Copyright (C) 1999-2011 Jean-Michel DOUDOUX  

 

22. La gestion dynamique des objets et l'introspection

 

chapitre 2 2

 

Depuis la version 1.1 de java, il est possible de créer et de gérer dynamiquement des objets.

L'introspection est un mécanisme qui permet de connaître le contenu d'une classe dynamiquement. Il permet notamment de savoir ce que contient une classe sans en avoir les sources. Ces mécanismes sont largement utilisés dans des outils de type IDE (Integrated Development Environnement : environnement de développement intégré).

Pour illustrer ces différents mécanismes, ce chapitre va construire une classe qui proposera un ensemble de méthodes pour obtenir des informations sur une classe.

Les différentes classes utiles pour l'introspection sont rassemblées dans le package java.lang.reflect.

Voici le début de cette classe qui attend dans son constructeur une chaîne de caractères précisant la classe sur laquelle elle va travailler.

Exemple ( code Java 1.1 ) :
import java.util.*;
import java.lang.reflect.*;
public class ClasseInspecteur {
  private Class classe;
  private String nomClasse;
  public ClasseInspecteur(String nomClasse) {
    this.nomClasse = nomClasse;
    try {
      classe = Class.forName(nomClasse);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Ce chapitre contient plusieurs sections :

 

22.1. La classe Class

Les instances de la classe Class sont des objets représentant les classes du langage. Il y aura une instance représentant chaque classe utilisée : par exemple la classe String, la classe Frame, la classe Class, etc ... . Ces instances sont crées automatiquement par la machine virtuelle lors du chargement de la classe. Il est ainsi possible de connaître les caractéristiques d'une classe de façon dynamique en utilisant les méthodes de la classe Class. Les applications telles que les debogueurs, les inspecteurs d'objets et les environnements de développement doivent faire une analyse des objets qu'ils manipulent en utilisant ces mécanismes.

La classe Class est définie dans le package java.lang.

La classe Class permet :

 

22.1.1. L'obtention d'un objet à partir de la classe Class

La classe Class ne possède pas de constructeur public mais il existe plusieurs façons d'obtenir un objet de la classe Class.

 

22.1.1.1. La détermination de la classe d'un objet

La méthode getClass() définit dans la classe Object renvoie une instance de la classe Class. Par héritage, tout objet java dispose de cette méthode.

Exemple ( code Java 1.1 ) :
package introspection;

public class TestGetClass {
   public static void main(java.lang.String[] args) {
      String chaine = "test";
      Class classe = chaine.getClass();
      System.out.println("classe de l'objet chaine = "+classe.getName());
   }
}

Résultat :
classe de l'objet chaine = java.lang.String

 

22.1.1.2. L'obtention d'un objet Class à partir d'un nom de classe

La classe Class possède une méthode statique forName() qui permet à partir d'une chaîne de caractères désignant une classe d'instancier un objet de cette classe et de renvoyer un objet de la classe Class pour cette classe.

Cette méthode peut lever l'exception ClassNotFoundException.

Exemple ( code Java 1.1 ) :
public class TestForName {
   public static void main(java.lang.String[] args) {
      try {
         Class classe = Class.forName("java.lang.String");
         System.out.println("classe de l'objet chaine = "+classe.getName());
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   }
}

Résultat :
classe de l'objet chaîne = java.lang.String

 

22.1.1.3. Une troisième façon d'obtenir un objet Class

Il est possible d'avoir un objet de la classe Class en écrivant type.class ou type est le nom d'une classe.

Exemple ( code Java 1.1 ) :
package introspection;
public class TestClass {
   public static void main(java.lang.String[] args) {
      Class c = Object.class;
      System.out.println("classe de Object  = "+c.getName());
   }
}

Résultat :
classe de Object  = java.lang.Object

 

22.1.2. Les méthodes de la classe Class

La classe Class fournie de nombreuses méthodes pour obtenir des informations sur la classe qu'elle représente. Voici les principales méthodes :

Méthodes Rôle
static Class forName(String) Instancie un objet de la classe dont le nom est fourni en paramètre et renvoie un objet Class la représentant
Class[] getClasses() Renvoie les classes et interfaces publiques qui sont membres de la classe
Constructor[] getConstructors() Renvoie les constructeurs publics de la classe
Class[] getDeclaredClasses() Renvoie un tableau des classes définies comme membre dans la classe
Constructor[] getDeclaredConstructors() Renvoie tous les constructeurs de la classe
Field[] getDeclaredFields() Renvoie un tableau de tous les attributs définis dans la classe
Method getDeclaredMethods() Renvoie un tableau de toutes les méthodes
Field getFields() Renvoie un tableau des attributs publics
Class[] getInterfaces() Renvoie un tableau des interfaces implémentées par la classe
Method getMethod() Renvoie un tableau des méthodes publiques de la classe incluant celles héritées
int getModifiers() Renvoie un entier qu'il faut décoder pour connaître les modificateurs de la classe
Package getPackage() Renvoie le package de la classe
Classe getSuperClass() Renvoie la classe mère de la classe
boolean isArray() Indique si la classe est un tableau
boolean IsInterface() Indique si la classe est une interface
Object newInstance() Permet de créer une nouvelle instance de la classe

 

22.2. La recherche des informations sur une classe

En utilisant les méthodes de la classe Class, il est possible d'obtenir quasiment toutes les informations sur une classe.

22.2.1. La recherche de la classe mère d'une classe

La classe Class possède une méthode getSuperClass() qui retourne un objet de la classe Class représentant la classe mère si elle existe sinon elle retourne null.

Pour obtenir toute la hiérarchie d'une classe il suffit d'appeler successivement cette méthode sur l'objet qu'elle a retourné.

Exemple ( code Java 1.1 ) : méthode qui retourne un vecteur contenant les classes mères
  public Vector getClassesParentes() {

    Vector cp = new Vector();

    Class sousClasse = classe;
    Class superClasse;

    cp.add(sousClasse.getName());
    superClasse = sousClasse.getSuperclass();
    while (superClasse != null) {
      cp.add(0,superClasse.getName());
      sousClasse = superClasse;
      superClasse = sousClasse.getSuperclass();
    }
    return cp;
  }

 

22.2.2. La recherche des modificateurs d'une classe

La classe Class possède une méthode getModifiers() qui retourne un entier représentant les modificateurs de la classe. Pour décoder cette valeur, la classe Modifier possède plusieurs méthodes qui attendent cet entier en paramètre et qui retourne un booléen selon leur fonction : isPublic(), isAbstract(), isFinal(), ...

La classe Modifier ne contient que des constantes et des méthodes statiques qui permettent de déterminer les modificateurs d'accès :

Méthode Rôle
boolean isAbstract(int) Renvoie true si le paramètre contient le modificateur abstract
boolean isFinal(int) Renvoie true si le paramètre contient le modificateur final
boolean isInterface(int) Renvoie true si le paramètre contient le modificateur interface
boolean isNative(int) Renvoie true si le paramètre contient le modificateur native
boolean isPrivate(int) Renvoie true si le paramètre contient le modificateur private
boolean isProtected(int) Renvoie true si le paramètre contient le modificateur protected
boolean isPublic(int) Renvoie true si le paramètre contient le modificateur public
boolean isStatic(int) Renvoie true si le paramètre contient le modificateur static
boolean isSynchronized(int) Renvoie true si le paramètre contient le modificateur synchronized
boolean isTransient(int) Renvoie true si le paramètre contient le modificateur transient
boolean isVolatile(int) Renvoie true si le paramètre contient le modificateur volatile

Ces méthodes étant static il est inutile d'instancier un objet de type Modifier pour utiliser ces méthodes.

Exemple ( code Java 1.1 ) :
  public Vector getModificateurs() { 
     
    Vector cp = new Vector(); 
    int m = classe.getModifiers(); 
    if (Modifier.isPublic(m)) 
      cp.add("public"); 
    if (Modifier.isAbstract(m)) 
      cp.add("abstract"); 
    if (Modifier.isFinal(m)) 
      cp.add("final"); 
         
    return cp; 
  }

 

22.2.3. La recherche des interfaces implémentées par une classe

La classe Class possède une méthode getInterfaces() qui retourne un tableau d'objet de type Class contenant les interfaces implémentées par la classe.

Exemple ( code Java 1.1 ) :
  public Vector getInterfaces() { 
     
    Vector cp = new Vector(); 
     
    Class[] interfaces = classe.getInterfaces(); 
    for (int i = 0; i < interfaces.length; i++) { 
      cp.add(interfaces[i].getName()); 
    } 
       
    return cp; 
  }

 

22.2.4. La recherche des champs publics

La classe Class possède une méthode getFields() qui retourne les attributs public de la classe. Cette méthode retourne un tableau d'objet de type Field.

La classe Class possède aussi une méthode getField() qui attend en paramètre un nom d'attribut et retourne un objet de type Field si celui-ci est défini dans la classe ou dans une de ses classes mères. Si la classe ne contient pas d'attribut dont le nom correspond au paramètre fourni, la méthode getField() lève une exception de la classe NoSuchFieldException.

La classe Field représente un attribut d'une classe ou d'une interface et permet d'obtenir des informations cet attribut. Elle possède plusieurs méthodes :

Méthode Rôle
String getName() Retourne le nom de l'attribut
Class getType() Retourne un objet de type Class qui représente le type de l'attribut
Class getDeclaringClass() Retourne un objet de type Class qui représente la classe qui définie l'attribut
int getModifiers() Retourne un entier qui décrit les modificateurs d'accès. Pour les connaître précisément il faut utiliser les méthodes static de la classe Modifier.
Object get(Object) Retourne la valeur de l'attribut pour l'instance de l'objet fourni en paramètre. Il existe aussi plusieurs méthodes getXXX() ou XXX représente un type primitif et qui la renvoie la valeur dans ce type.

Exemple ( code Java 1.1 ) :
  public Vector getChampsPublics() { 
     
    Vector cp = new Vector(); 

    Field[] champs = classe.getFields(); 
    for (int i = 0; i < champs.length; i++) 
      cp.add(champs[i].getType().getName()+" "+champs[i].getName()); 
    return cp; 
  }

 

22.2.5. La recherche des paramètres d'une méthode ou d'un constructeur

L'exemple ci-dessous présente une méthode qui permet de formater sous forme de chaîne de caractères les paramètres d'une méthode fournis sous la forme d'un tableau d'objets de type Class.

Exemple ( code Java 1.1 ) :
  private String rechercheParametres(Class[] classes) { 
                 
    StringBuffer param = new StringBuffer("("); 
                 
    for (int i = 0; i < classes.length; i ++) { 
       
      param.append(formatParametre(classes[i].getName())); 
      if (i < classes.length - 1)  
        param.append(", "); 
                    
    } 
    param.append(")"); 
     
    return param.toString(); 
            
  }

La méthode getName() de la classe Class renvoie une chaîne de caractères formatée qui précise le type de la classe. Ce type est représenté par une chaîne de caractères qu'il faut décoder pour obtenir le type.

Si le type de la classe est un tableau alors la chaîne commence par un nombre de caractère '[' correspondant à la dimension du tableau.

Ensuite la chaîne contient un caractère qui précise un type primitif ou un objet. Dans le cas d'un objet, le nom de la classe de l'objet avec son package complet est contenu dans la chaîne suivi d'un caractère ';'.

Caractère

Type

B byte
C char
D double
F float
I int
J long
Lclassname; classe ou interface
S short
Z boolean

Exemple :

La méthode getName() de la classe Class représentant un objet de type float[10][5] renvoie « [[F »

Pour simplifier les traitements, la méthode formatParametre() ci-dessous retourne une chaîne de caractères qui décode le contenu de la chaîne retournée par la méthode getName() de la classe Class.

Exemple :
  private String formatParametre(String s) { 
   
    if (s.charAt(0) == '[') { 
     
      StringBuffer param = new StringBuffer(""); 
     
      int dimension = 0; 
      while (s.charAt(dimension) == '[') dimension++; 
       
      switch(s.charAt(dimension)) { 
        case 'B' : param.append("byte");break; 
        case 'C' : param.append("char");break; 
        case 'D' : param.append("double");break; 
        case 'F' : param.append("float");break; 
        case 'I' : param.append("int");break; 
        case 'J' : param.append("long");break; 
        case 'S' : param.append("short");break; 
        case 'Z' : param.append("boolean");break; 
        case 'L' : param.append(s.substring(dimension+1,s.indexOf(";"))); 
      } 
       
      for (int i =0; i < dimension; i++) 
        param.append("[]"); 
                 
      return param.toString();  
    }           
    else return s; 
                 
  }

 

22.2.6. La recherche des constructeurs de la classe

La classe Class possède une méthode getConstructors() qui retourne un tableau d'objet de type Constructor contenant les constructeurs de la classe.

La classe Constructor représente un constructeur d'une classe. Elle possède plusieurs méthodes :

Méthode Rôle
String getName() Retourne le nom du constructeur
Class[] getExceptionTypes() Retourne un tableau de type Class qui représente les exceptions qui peuvent être propagées par le constructeur
Class[] getParametersType() Retourne un tableau de type Class qui représente les paramètres du constructeur
int getModifiers() Retourne un entier qui décrit les modificateurs d'accès. Pour les connaître précisément il faut utiliser les méthodes static de la classe Modifier.
Object newInstance(Object[]) Instancie un objet en utilisant le constructeur avec les paramètres fournis à la méthode

Exemple ( code Java 1.1 ) :
  public Vector getConstructeurs() { 
     
    Vector cp = new Vector(); 
    Constructor[] constructeurs = classe.getConstructors(); 
    for (int i = 0; i < constructeurs.length; i++) { 
      cp.add(rechercheParametres(constructeurs[i].getParameterTypes())); 
     
    }
             
    return cp; 
  }

L'exemple ci-dessus utilise la méthode rechercherParamètres() définie précédemment pour simplifier les traitements.

22.2.7. La recherche des méthodes publiques

Pour consulter les méthodes d'un objet, il faut obtenir sa classe et lui envoyer le message getMethod(), qui renvoie les méthodes publiques qui sont déclarées dans la classe et qui sont héritées des classes mères.

Elle renvoie un tableau d'instances de la classe Method du package java.lang.reflect.

Une méthode est caractérisée par un nom, une valeur de retour, une liste de paramètres, une liste d'exceptions et une classe d'appartenance.

La classe Method contient plusieurs méthodes :

Méthode Rôle
Class[] getParameterTypes Renvoie un tableau de classes représentant les paramètres.
Class getReturnType Renvoie le type de la valeur de retour de la méthode.
String getName() Renvoie le nom de la méthode
int getModifiers() Renvoie un entier qui représente les modificateurs d'accès
Class[] getExceptionTypes Renvoie un tableau de classes contenant les exceptions propagées par la méthode
Class getDeclaringClass[] Renvoie la classe qui définit la méthode

Exemple ( code Java 1.1 ) :
  public Vector getMethodesPubliques() { 
     
    Vector cp = new Vector(); 
    Method[] methodes = classe.getMethods(); 
    for (int i = 0; i < methodes.length; i++) { 
      StringBuffer methode = new StringBuffer(); 
                 
      methode.append(formatParametre(methodes[i].getReturnType().getName())); 
      methode.append(" "); 
      methode.append(methodes[i].getName()); 
      methode.append(rechercheParametres(methodes[i].getParameterTypes())); 
       
      cp.add(methode.toString()); 
       
    } 
    return cp; 
  }

L'exemple ci-dessus utilise les méthodes formatParametre() et rechercherParametres() définies précédemment pour simplifier les traitements.

 

22.2.8. La recherche de toutes les méthodes

Pour consulter toutes les méthodes d'un objet, il faut obtenir sa classe et lui envoyer le message getDeclaredMethods(), qui renvoie toutes les méthodes qui sont déclarées dans la classe et qui sont héritées des classes mères quelque soit leur accessibilité.

Elle renvoie un tableau d'instances de la classe Method du package java.lang.reflect.

Exemple :
  public Vector getMethodes() { 
     
    Vector cp = new Vector(); 
    Method[] methodes = classe.getDeclaredMethods(); 
    for (int i = 0; i < methodes.length; i++) { 
      StringBuffer methode = new StringBuffer(); 
                 
      methode.append(formatParametre(methodes[i].getReturnType().getName())); 
      methode.append(" "); 
      methode.append(methodes[i].getName()); 
      methode.append(rechercheParametres(methodes[i].getParameterTypes())); 
       
      cp.add(methode.toString()); 
       
    } 

    return cp; 
  }

L'exemple ci-dessus utilise les méthodes formatParametre() et rechercherParametres() définies précédemment pour simplifier les traitements.

 

22.3. La définition dynamique d'objets

 

22.3.1. La définition d'objets grâce à la classe Class

 

 

en construction
Cette section sera développée dans une version future de ce document

 

22.3.2. L'exécution dynamique d'une méthode

 

 

en construction
Cette section sera développée dans une version future de ce document

 


  21. L'interaction avec le réseau 23. L'appel de méthodes distantes : RMI Imprimer Sommaire Consulter avec table des matières Développons en Java   v 1.60  
Copyright (C) 1999-2011 Jean-Michel DOUDOUX