API Reflection
L'API
L'API java.lang.reflect peut avant tout être utile pour l'introspection, c'est à dire la découverte des caractéristiques d'une classe (classe mère, champs, méthodes, ...). D'une utilité un peu plus avancée, elle permet d'instancier des classes de manière dynamique, en agissant sur ses champs, en appelant ses méthodes ...
La réflexion est également potentiellement intéressante pour l'écriture d'un générateur de code générique pour un ensemble de classe.
Cette API sert également dans le processus de sérialisation d'un bean Java ou dans la génération du code de création d'une table en base de données pour la persistance de la classe cible (ce qu'utilise toutes les APIs de mapping Objet/Relationnel).
Nous allons maintenant voir les classes que nous propose cette API, mais avant cela, nous allons voir la classe Class, qui ne fait partie à proporement parler de l'API, mais qui se revèle indspensable pour l'utiliser.
La classe Class
C'est la classe de base sur laquelle repose l'API reflect. En effet, dans toutes les classes et méthodes que nous rencontrerons plus tard, elle sera présente.
En ce qui concerne la réflection, voici les méthodes importantes qu'elle met à disposition:
| Field getField(String name) | Renvoie un objet Field correspondant au champ "name" |
| Field[] getFields() | Renvoie l'ensemble des champs publics sous la forme d'un tableau |
| Method getMethod(String name, Class[] parameterTypes) | Renvoie un objet Method correspondant à la méthode "name" avec les paramètres définis par le tableau parameterTypes |
| Method[] getMethods() | Renvoie l'ensemble des méthodes publiques sous la forme d'un tableau |
| Constructor getConstructor(Class[] parameterTypes) | Renvoie un objet Constructor correspondant au constructeur avec les paramètres defines par le tableau parameterTypes |
| Constructor[] getConstructors() | Renvoie l'ensemble des constructeurs sous la forme d'un tableau |
| java.lang.Class[] getInterfaces() | Renvoie l'ensemble des interfaces implémentées |
| java.lang.Class getSuperclass() | Renvoie la classe mère |
| java.lang.Package getPackage() | Renvoie un objet Package correspondant au paquetage dans lequel se trouve la classe |
On constate bien que c'est à partir d'un objet de type Class que l'on peut récupérer les objets de l'API java.lang.reflect.
A noter que, pour des raisons de lisibilité, les noms de classes non préfixés par le paquetage complet appartiennent au paquetage java.lang.reflect.
Exemples
Mettons en pratique quelques unes de ces méthodes:
- Afficher les interfaces
package beginning;
import java.util.HashMap;
public class SampleInterface {
public static void main(String[] args) {
HashMap b = new HashMap();
printInterfaceNames(b);
}
/**
* Affiche les interfaces implémentées par l'objet
*
* @param o
* Object
*/
static void printInterfaceNames(Object o) {
Class c = o.getClass();
Class[] theInterfaces = c.getInterfaces();
for (int i = 0; i < theInterfaces.length; i++) {
String interfaceName = theInterfaces[i].getName();
System.out.println(interfaceName);
}
}
}
package beginning;
import javax.swing.JButton;
public class SuperClass {
public static void main(String[] args) {
JButton b = new JButton();
printSuperclasses(b);
}
/**
* Affiche les superClasses de l'objet
*
* @param o
* Object
*/
static void printSuperclasses(Object o) {
Class subclass = o.getClass();
Class superclass = subclass.getSuperclass();
while (superclass != null) {
String className = superclass.getName();
System.out.println(className);
subclass = superclass;
superclass = subclass.getSuperclass();
}
}
}
La classe Field
La méthode getField() de la classe Class nous renvoie un objet de type Field. A partir de cet objet, nous pouvons avoir des informations sur un champ donné de la classe. Voici les méthodes que cete classe nous propose:| String getName() | Donne le nom du champ |
| Class getType() | Donne le type du champ |
| int getModifier() | Rentourne un entier représentant la visibilité du champ (private, protected, public). Permet également d'avoir d'autres informations comme static |
-
Affiche les informations sur un champ donné
package beginning; import java.awt.GridBagConstraints; import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class SampleField { public static void main(String[] args) { GridBagConstraints g = new GridBagConstraints(); printFieldNames(g); } /** * Affiche les noms et les informations sur les champs d'un objet * @param o Object */ static void printFieldNames(Object o) { Class c = o.getClass(); Field[] publicFields = c.getFields(); for (int i = 0; i < publicFields.length; i++) { String fieldName = publicFields[i].getName(); Class typeClass = publicFields[i].getType(); String fieldType = typeClass.getName(); int m = publicFields[i].getModifiers(); System.out.print("Name: " + fieldName + ", Type: " + fieldType + ", Modifiers: "); if (Modifier.isFinal(m)) System.out.print("final "); if (Modifier.isProtected(m)) System.out.print("protected "); if (Modifier.isPrivate(m)) System.out.print("private "); if (Modifier.isPublic(m)) System.out.print("public "); if (Modifier.isStatic(m)) System.out.print("static "); System.out.println(""); } } }
La classe Method
Nous avons vu que les méthodes getMethod(String name, Class[] parameterTypes) et getMethods() nous renvoient respectivement un objet Method ou un tableau de Method. Cette classe permet de récupérer des informations sur une méthode. Nous verrons plus tard qu'elle met également à disposition des méthodes d'invocation.Les méthodes intéressantes:
| Class[] getExceptionTypes() | Retourne un tableau contenant les classes Exception susceptibles d'être lancées |
| String getName() | Doone le nom de la méthode |
| Class getReturnType() | Retourne la classe du paramètre retourné par la méthode |
| Class[] getParameterTypes() | Retourne un tableau contenant les classes des paramètres de la méthode |
| int getModifiers() | Retourne un entier représentant la visibilité du champ (private, protected, public). Permet également d'avoir d'autres informations comme static |
L'exemple suivant permet d'afficher les nom et les paramètres des méthodes publiques d'un objet passé en paramètre:
package beginning;
import java.awt.Polygon;
import java.lang.reflect.Method;
public class SampleMethod {
public static void main(String[] args) {
Polygon p = new Polygon();
showMethods(p);
}
/**
* Affiche le nom et les paramètres des méthodes publiques d'un objet
* @param o Object
*/
static void showMethods(Object o) {
Class c = o.getClass();
Method[] theMethods = c.getMethods();
for (int i = 0; i < theMethods.length; i++) {
String methodString = theMethods[i].getName();
System.out.println("Name: " + methodString);
String returnString = theMethods[i].getReturnType().getName();
System.out.println(" Return Type: " + returnString);
Class[] parameterTypes = theMethods[i].getParameterTypes();
System.out.print(" Parameter Types:");
for (int k = 0; k < parameterTypes.length; k++) {
String parameterString = parameterTypes[k].getName();
System.out.print(" " + parameterString);
}
System.out.println();
}
}
}
La classe Constructor
Cette classe, à l'instar de la classe Method, permet de récupérer des informations sur les constructeurs.| Class[] getExceptionTypes() | Retourne un tableau contenant les classes Exception susceptibles d'être lancées |
| String getName() | Retourne le nom de la méthode |
| Class[] getParameterTypes() | Retourne un tableau contenant les classes des paramètres de la méthode |
| int getModifiers() | Retourne un entier représentant la visibilité du champ (private, protected, public). |
package beginning;
import java.awt.Rectangle;
import java.lang.reflect.Constructor;
public class SampleConstructor {
public static void main(String[] args) {
Rectangle r = new Rectangle();
showConstructors(r);
}
/**
* Affiche les parmaètres des différents constructeurs publiques d'une classe
* @param o Object
*/
static void showConstructors(Object o) {
Class c = o.getClass();
Constructor[] theConstructors = c.getConstructors();
for (int i = 0; i < theConstructors.length; i++) {
System.out.print("( ");
Class[] parameterTypes = theConstructors[i].getParameterTypes();
for (int k = 0; k < parameterTypes.length; k++) {
String parameterString = parameterTypes[k].getName();
System.out.print(parameterString + " ");
}
System.out.println(")");
}
}
}
La classe Modifier
Les méthodes getModifier(s)() des classes Class, Field, Method et Constructor renvoient un entier représentant la visibilité du champ. Afin de pouvoir l'expoliter, le classe Modifier nous propose des méthodes static renvoyant un booléen permettant de tester cet entier.Voici les méthodes les plus intéressantes:
| boolean static isFinal(int mod) | Détermine si le code transmis intègre la particularité "final" |
| boolean static isStatic(int mod) | Détermine si le code transmis intègre la particularité "static" |
| boolean static isAbstract(int mod) | Détermine si le code transmis intègre la particularité "abstract" |
| boolean static isPublic(int mod) | Détermine si le code transmis intègre une visibilité "public" |
| boolean static isProtected(int mod) | Détermine si le code transmis intègre une visibilité "protected" |
| boolean static isPrivate(int mod) | Détermine si le code transmis intègre une visibilité "private" |
package beginning;
import java.lang.reflect.Modifier;
public class ClassModifier {
public static void main(String[] args) {
String s = new String();
printModifiers(s);
}
/**
* Affiche les modificateurs de la classe
*
* @param o
* Object
*/
public static void printModifiers(Object o) {
Class c = o.getClass();
int m = c.getModifiers();
if (Modifier.isPublic(m))
System.out.println("public");
if (Modifier.isAbstract(m))
System.out.println("abstract");
if (Modifier.isFinal(m))
System.out.println("final");
}
}