|
|
|
|
|
|
Développons en Java v 1.60 | |
| Copyright (C) 1999-2011 Jean-Michel DOUDOUX |
![]() |
![]() |
![]() |
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 :
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 :
La classe Class ne possède pas de constructeur public mais il existe plusieurs façons d'obtenir un objet de la classe Class.
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 |
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 |
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 |
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 |
En utilisant les méthodes de la classe Class, il est possible d'obtenir quasiment toutes les informations sur 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;
} |
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;
} |
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;
} |
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;
} |
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;
} |
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.
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.
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.
![]() |
|
Cette section sera développée dans une version future de ce document
|
![]() |
|
Cette section sera développée dans une version future de ce document
|
|
|
|
|
|
|
Développons en Java v 1.60 | ||
| Copyright (C) 1999-2011 Jean-Michel DOUDOUX |