Dans cette étude, nous allons nous pencher sur l'API de réflexion de Java, gérée par les classes du paquetage java.lang.reflect. Comme son nom l'indique, la reflexion est la possibilité, pour une classe ou un objet, de s'examiner. Elle permet au code Java d'étudier un objet (plus précisément la classe de l'objet) et de déterminer sa structure. Dans les limites imposées par le gestionnaire de sécurité, nous pouvons trouver les constructeurs, les méthodes, et les attributs d'une classe ainsi que leurs valeurs.
Nous pouvons même changer la valeur des attributs, invoquer dynamiquement des méthodes, et construire de nouveaux objets, comme si Java possédait des pointeurs sur les attributs et les méthodes.
Nous pouvons réaliser tout cela sur des objets que notre code n'a pas encore vu.
.
La bibliothèque de reflexion constitue une boîte à outils riche et élaborée pour écrire des programmes qui manipules dynamiquement du code Java. Cette fonctionnalité est très largement utilisée dans JavaBeans, l'architecture des composants Java. Au moyen de la reflexion, Java est capable de supporter des outils comme ceux auxquels les utilisateurs de Visual Basic sont habitués. Précisément, lorsque de nouvelles classes sont ajoutées au moment de la conception ou de l'exécution, des outils de développement d'application rapide peuvent se renseigner dynamiquement sur les capacités des classes qui ont été ajoutées.
Une programme qui peut analyser les capacités des classes est appelé réflecteur.
.
Avant de déterminer les éléments de la classe, comme les attributs, les méthodes et les constructeurs, il faut préalablement connaître le nom même de la classe d'un objet. Dans ce chapitre, nous allons voir comment récupérer cette information.
Lorsque le programme est lancé, le système d'exécution de Java gère, pour tous les objets, ce que l'on appelle "L'identification de type à l'exécution". Cette information mémorise la classe à laquelle chaque objet appartient. L'information de type au moment de l'exécution est employée par la machine virtuelle pour sélectionner les méthodes correctes à exécuter.
Vous pouvez accéder à cette information en travaillant avec une classe Java particulière, baptisée singulièrement Class. La méthode getClass() de la classe Object renvoie une instance de type Class :
Tout comme un objet Personne décrit les propriétés d'une personne particulier, un objet Class décrit les propriétés d'une classe particulière. La méthode la plus utilisée de Class est sans contexte getName(), qui renvoie le nom de la classe :
init:
deps-jar:
Compiling 1 source file to C:\netbeans-5.0\travail\Test\build\classes
compile:
run:
test.Personne
BUILD SUCCESSFUL (total time: 3 seconds)
package test; public class Main { public static void main(String[] args) { Personne p = new Personne("REMY", "Emmanuel"); Class cl = p.getClass(); System.out.println(cl.getName()); } } class Personne { private String nom, prénom; public Personne(String nom, String prénom) { this.nom = nom; this.prénom = prénom; } }
Vous pouvez aussi obtenir un objet Class correspondant à une chaîne de caractères, à l'aide de la méthode statique forName() :
init:
deps-jar:
Compiling 1 source file to C:\netbeans-5.0\travail\Test\build\classes
compile:
run:
test.Personne
BUILD SUCCESSFUL (total time: 3 seconds)
package test; public class Main { public static void main(String[] args) throws ClassNotFoundException { String nomClasse = "test.Personne"; Class cl = Class.forName(nomClasse); System.out.println(cl.getName()); } } class Personne { private String nom, prénom; public Personne(String nom, String prénom) { this.nom = nom; this.prénom = prénom; } }
Cette technique est utilisée si le nom de la classe est stockée dans une chaîne de caractères qui peut changer à l'exécution. Cela fonctionne lorsque nomClasse est bien le nom d'une classe ou d'une interface. Dans le cas contraire, la méthode forName() lance une exception vérifiée.
Une troisième technique emploie un raccourci pratique pour obtenir un objet de type Class. En effet, si T est d'un type Java quelconque, alors T.class représente l'objet classe correspondant.
Class cl1 = Personne.class ; // Pour le suffixe .class il faut importer java.util.*;
Class cl2 = int.class ;
Class cl3 = Double[].class ;
Remarquez qu'un objet Class désigne en réalité un type, qui n'est pas nécessairement une classe. Par exemple, int n'est pas une classe, mais int.class est pourtant un objet de type Class.
init:
deps-jar:
Compiling 1 source file to L:\BTS IRIS\TP Java\reflexion\build\classes
compile:
run:
Nom de la classe :
java.awt.Point
Classe de base --------------------------------
class java.awt.geom.Point2D
Attributs -------------------------------------
public int x;
public int y;
private static final long serialVersionUID;
Constructeurs ---------------------------------
public java.awt.Point(int, int);
public java.awt.Point();
public java.awt.Point(java.awt.Point);
Méthodes --------------------------------------
boolean public equals(java.lang.Object);
class java.lang.String public toString();
class java.awt.Point public getLocation();
double public getX();
double public getY();
void public move(int, int);
void public setLocation(double, double);
void public setLocation(int, int);
void public setLocation(java.awt.Point);
void public translate(int, int);
BUILD SUCCESSFUL (total time: 2 seconds)
Les trois premières caractéristiques d'une classe sont ses attributs (ou champs), ses méthodes et ses constructeurs. Pour des raisons de description ou d'accès à un objet, elles sont représentées dans l'API de reflexion par des classes séparées. Nous pouvont rechercher ces membres de classe en utilisant l'objet Class :
La classe Class propose deux paires de méthodes permettant d'atteindre chaque caractéristiques. Une paire autorise l'accès aux fonctionnalités publiques d'une classe (y compris celles héritées de ses classes mères), l'autre permettant l'accès à tout élément public ou non, déclaré directement à l'intérieur de la classe (mais pas aux fonctionnalités hérités), selon les considérations de sécurité. Voici quelques exemples :
Chaque paire de méthodes comporte une méthode permettant de lister tous les éléments à la fois, par exemple getFields(), et une méthode permettant de rechercher un élément particulier par son nom et, pour les méthodes et les constructeurs, par signature, par exemple, getField(), qui prend le nom de l'attribut comme argument.
Les trois classes Field, Method, Constructor, qui se trouvent dans le paquetage java.lang.reflect, décrivent respectivement les attributs, les méthodes et les constructeurs d'une classe. Ces trois classes disposent d'une méthode getName() qui renvoie le nom de l'élément.
Les trois classes possèdent également une méthode appelée getModifiers() : elle renvoie un entier dont les bits sont utilisés comme sémaphores pour décrire les modificateurs spécifiés, tels que public ou static. Vous pouvez alors utiliser les méthodes statiques de la classe Modifier du paquetage java.lang.reflect pour analyser les entiers renvoyés par getModifiers(). Par exemple, il existe des méthodes telles que isPublic(), isPrivate() ou isFinal() pour déterminer si un constructeur ou une méthode a été déclarée public, private ou final.
Il vous suffit d'appeler la méthode appropriée de Modifier et de l'utiliser sur l'entier renvoyé par getModifiers(). Il est également possible d'employer Modifier.toString() pour afficher l'ensemble des modificateurs.
import java.util.*; import java.lang.reflect.*; import static java.lang.System.*; public class Reflexion { public static void main(String[] args) throws Exception { Scanner clavier = new Scanner(in); out.println("Nom de la classe : "); String nomClasse = clavier.next(); Class classe = Class.forName(nomClasse); // affiche la superclasse out.println("Classe de base --------------------------------"); out.println(" "+classe.getSuperclass()); // affiche tous les attributs out.println("Attributs -------------------------------------"); for (Field attribut : classe.getDeclaredFields()) { out.print(" "+Modifier.toString(attribut.getModifiers())); out.println(" "+attribut.getType()+" "+attribut.getName()+";"); } // affiche tous les consructeurs out.println("Constructeurs ---------------------------------"); for (Constructor constructeur : classe.getDeclaredConstructors()) { out.print(" "+Modifier.toString(constructeur.getModifiers())); out.print(" "+constructeur.getName()+"("); Class[] typeParamètres = constructeur.getParameterTypes(); for (int i=0; i<typeParamètres.length; i++) { if (i>0) out.print(", "); out.print(typeParamètres[i].getName()); } out.println(");"); } // affiche toutes les méthodes out.println("Méthodes --------------------------------------"); for (Method méthode : classe.getDeclaredMethods()) { out.print(" "+méthode.getReturnType()+" "); out.print(Modifier.toString(méthode.getModifiers())); out.print(" "+méthode.getName()+"("); Class[] typeParamètres = méthode.getParameterTypes(); for (int i=0; i<typeParamètres.length; i++) { if (i>0) out.print(", "); out.print(typeParamètres[i].getName()); } out.println(");"); } } }
Les accès à l'API de reflexion sont contrôlés par un gestionnaire de sécurité. Une application complètement sécurisée a accès à toutes les fonctionnalités vues précédemment ; elle peut accéder aux membres de classes au niveau de restriction accordé au code compris dans sa portée. Il est toutefois possible de fournir des accès privilégiés au code, afin qu'il puisse utiliser l'API de réflexion et accéder à des memebres protégés et privés d'autres classes, ce qui est normalement interdit par le langage Java.
Les classes Field, Method et Constructor sont toutes des extensions de la classe de base AccessibleObject. La classe AccessibleObject possède une méthode fondamentale, appelée setAccessible(), qui permet de désactiver la sécurité d'accès. à un membre particulier d'une classe. cela semble trop facile. C'est effectivement facile, mais le fait que cette méthode vous permettent ou pas de désactiver la sécurité est une fonctionnalité du gestionnaire de sécurité Java et des règles associées. Vous pouvez le faire uniquement dans une application Java qui fonctionne sans aucune règle de sécurité.
La classe java.lang.reflect.Field représente les attributs des objets. Field possède un jeu complet de méthode d'accès surchargées pour tous les types de base, par exemple getInt() et setInt(), getBoolean() et setBoolean() et des méthodes get() et set() pour accéder à des attributs qui font références à des objets.
import java.lang.reflect.*; import static java.lang.System.*; class Banque { public int balance = 25; } public class Main { public static void main(String[] args) throws Exception { Banque compte = new CompteBancaire(); Field at = Banque.class.getField("balance"); int balance = at.getInt(compte); out.println("Balance = "+balance); at.setInt(compte, 42); out.println("Balance = "+at.getInt(compte)); } }
Dans cet exemple, nous sommes supposés déjà connaître la structure de l'objet Banque. En règle général, nous devrions pouvoir récupérer cette information à partir de l'objet même.
Toutes les données des méthodes d'accès de Field prennent une référence sur l'objet auquel nous voulons accéder. Dans le code ci-dessus, la méthode getField() renvoie un objet Field représentant l'attribut balance de la classe Banque ; cet objet Field ne fait pas référence à un objet Banque particulier. Par conséquent, pour lire ou modifier un objet de type Banque spécifique, nous appelons getInt() et setInt() avec une référence à compte, qui est le compte précis sur lequel nous désirons travailler.
Ici, nous ne faisons rien d'autre que ce que nous aurions pu faire avec un code statique à la compilation.
L'important, c'est que nous pouvons accéder à balance en cours d'exécution, dans une classe chargée dynamiquement.
init:
deps-jar:
Compiling 1 source file to C:\netbeans-5.0\travail\Test\build\classes
compile:
run:
46
46
46
BUILD SUCCESSFUL (total time: 3 seconds)
Dans la section précédente, nous avons vu comment trouver le nom et le type des attributs de n'importe quel objet en suivant la procédure suivante :
Dans cette section, nous allons franchir une étape supplémentaire et étudier le contenu des attributs.
Bien entendu, il est facile de lire le contenu d'un champ spécifique d'un objet dont le nom et le type sont connus lors de l'écriture du programme. Mais la réflexion permet de lire les attributs des objets qui n'étaient pas connus au moment de la compilation.
A cet égard, la méthode essentielle est la méthode get() de la classe Field. Si attribut est un objet de type Field obtenu au moyen de la méthode getDeclaredFields() et moi un objet de la classe dont attribut est un attribut, alors attribut.get(moi) renvoie un objet dont la valeur est la valeur courante de l'attribut de l'objet moi :
package test; import java.lang.reflect.*; public class Main { public static void main(String[] args) throws Exception { Personne moi = new Personne("REMY", "Emmanuel", 46); Class classe = moi.getClass(); Field attribut = classe.getDeclaredField("nom"); Object valeur = attribut.get(moi); System.out.println(valeur); } } class Personne { private String nom, prénom; private int âge; public Personne(String nom, String prénom, int âge) { this.nom = nom; this.prénom = prénom; this.âge = âge; } } // Résultat : Exception de type IllegalAccessException
En réalité, ce code pose un problème. Comme l'attribut moi est privé, la méthode get() déclenche une exception IllegalAccessException. Cette méthode ne peut être employée que pour obtenir les valeurs des champs accessibles. Le mécanisme de sécurité Java vous permet de connaître les champs d'un objet, mais pas de lire la valeur de ces champs si vous n'avez pas une autorisation d'accès.
Par défaut, le mécanisme de réflexion respecte le contrôle des accès. Néanmoins, si un programme Java n'est pas contrôlé par un gestionnaire de sécurité qui le lui interdit, il peut outrepasser son droit d'accès. Pour cela, il faut invoquer la méthode setAccessible() d'un objet Field, Method ou Constructor :
attribut.setAccessible(true) ;
La méthode setAccessible() se trouve dans la classe AccessibleObject, qui est la superclasse commune des classes Field, Method ou Constructor. Cette fonctionnalité est destinée au débogage, au stockage permanent et à des mécanismes similaires.
package test; import java.lang.reflect.*; public class Main { public static void main(String[] args) throws Exception { Personne moi = new Personne("REMY", "Emmanuel", 46); Class classe = moi.getClass(); Field attribut = classe.getDeclaredField("nom"); attribut.setAccessible(true); Object valeur = attribut.get(moi); System.out.println(valeur); } } class Personne { private String nom, prénom; private int âge; public Personne(String nom, String prénom, int âge) { this.nom = nom; this.prénom = prénom; this.âge = âge; } } // Résultat : REMY
La méthode get() pose un second problème. Dans notre exemple, l'attribut nom est de type String, il est donc possible de récupérer la valeur en tant que Object. Mais supposons que nous désirions étudier l'attribut âge. Celui-ci est de type primitif int, et les nombres ne sont pas des objets en Java. Il existe deux solutions :
L'exemple ci-dessous permet d'exploiter ces deux possibilités, ou même de travailler avec la classe de base Object :
package test; import java.lang.reflect.*; public class Main { public static void main(String[] args) throws Exception { Personne moi = new Personne("REMY", "Emmanuel", 46); Class classe = moi.getClass(); Field attribut = classe.getDeclaredField("âge"); attribut.setAccessible(true); Object objet = attribut.get(moi); // première solution System.out.println(objet); int entier = (Integer) attribut.get(moi); // deuxième solution System.out.println(entier); int valeur = attribut.getInt(moi); // troisième solution System.out.println(valeur); } } class Personne { private String nom, prénom; private int âge; public Personne(String nom, String prénom, int âge) { this.nom = nom; this.prénom = prénom; this.âge = âge; } }
Bien entendu, il est possible de modifier des valeurs obtenues. L'appel attribut.set(objet, valeur) affecte une nouvelle valeur à l'attribut de l'objet.
La création d'une nouvelle instance (nouvel objet) est réalisé au travers de la méthode newInstance() de la classe Class. Cette méthode est étudiée un peu plus loin.
§
init:
deps-jar:
Compiling 1 source file to L:\BTS IRIS\TP Java\reflexion\build\classes
compile:
run:
Nom de la classe :
java.awt.Point
Classe de base --------------------------------
class java.awt.geom.Point2D
Attributs -------------------------------------
public int x = 0;
public int y = 0;
private static final long serialVersionUID = -5276940640259749850;
Constructeurs ---------------------------------
public java.awt.Point(int, int);
public java.awt.Point();
public java.awt.Point(java.awt.Point);
Méthodes --------------------------------------
boolean public equals(java.lang.Object);
class java.lang.String public toString();
class java.awt.Point public getLocation();
double public getX();
double public getY();
void public move(int, int);
void public setLocation(double, double);
void public setLocation(int, int);
void public setLocation(java.awt.Point);
void public translate(int, int);
BUILD SUCCESSFUL (total time: 3 seconds)
import java.util.*; import java.lang.reflect.*; import static java.lang.System.*; public class Reflexion { public static void main(String[] args) throws Exception { Scanner clavier = new Scanner(in); out.println("Nom de la classe : "); String nomClasse = clavier.next(); Class classe = Class.forName(nomClasse); Object objet = classe.newInstance(); // affiche la superclasse out.println("Classe de base --------------------------------"); out.println(" "+classe.getSuperclass()); // affiche tous les attributs out.println("Attributs -------------------------------------"); for (Field attribut : classe.getDeclaredFields()) { out.print(" "+Modifier.toString(attribut.getModifiers())); out.print(" "+attribut.getType()+" "+attribut.getName()+" = "); attribut.setAccessible(true); out.println(attribut.get(objet)+";"); } // affiche tous les consructeurs out.println("Constructeurs ---------------------------------"); for (Constructor constructeur : classe.getDeclaredConstructors()) { out.print(" "+Modifier.toString(constructeur.getModifiers())); out.print(" "+constructeur.getName()+"("); Class[] typeParamètres = constructeur.getParameterTypes(); for (int i=0; i<typeParamètres.length; i++) { if (i>0) out.print(", "); out.print(typeParamètres[i].getName()); } out.println(");"); } // affiche toutes les méthodes out.println("Méthodes --------------------------------------"); for (Method méthode : classe.getDeclaredMethods()) { out.print(" "+méthode.getReturnType()+" "); out.print(Modifier.toString(méthode.getModifiers())); out.print(" "+méthode.getName()+"("); Class[] typeParamètres = méthode.getParameterTypes(); for (int i=0; i<typeParamètres.length; i++) { if (i>0) out.print(", "); out.print(typeParamètres[i].getName()); } out.println(");"); } } }
La classe java.lang.reflect.Method représente une méthode statique (de classe) ou une simple méthode issue d'un objet.
init:
deps-jar:
Compiling 1 source file to L:\BTS IRIS\TP Java\test\build\classes
compile:
run:
Nom : REMY
Age = 46
BUILD SUCCESSFUL (total time: 0 seconds)
Les pointeurs de méthodes permettent de fournir l'adresse d'une méthode à une autre méthode, afin que la seconde puisse appeler la première. Pour voir à l'oeuvre les pointeurs de méthodes, rappelez-vous que vous pouvez inspecter un attribut à l'aide de la méthode get() de la classe Field. Pour sa part, la classe Method dispose d'une méthode invoke() permettant d'appeler la méthode enveloppée dans l'objet Method. La signature de la méthode invoke() est la suivante :
Object Method.invoke(Object obj, Object... args) ;
Le premier paramètre est implicite, il s'agit de l'objet (si il est créé) qui possède cette méthode. Les autres objets fournissent les éventuels paramètres explicites de la méthode invoquée.
Dans le cas d'une méthode statique, le premier paramètre est ignoré, il peut recevoir la valeur null.
.
Voici un exemple qui permet de lancer la méthode getNom() (sans arguments) de la classe Personne :
package test; import java.lang.reflect.*; public class Main { public static void main(String[] args) throws Exception { Personne moi = new Personne("REMY", "Emmanuel", 46); Class classe = moi.getClass(); Method nom = classe.getDeclaredMethod("getNom"); System.out.println("Nom : "+nom.invoke(moi)); int âge = (Integer) classe.getDeclaredMethod("getAge").invoke(moi); System.out.println("Age = "+âge); } } class Personne { private String nom, prénom; private int âge; public Personne(String nom, String prénom, int âge) { this.nom = nom; this.prénom = prénom; this.âge = âge; } public String getNom() { return nom; } public String getPrénom() { return prénom; } public int getAge() { return âge; } }
A l'image des méthodes get() et set() de l'attribut Field, un problème se pose si le paramètre ou le type de résultat est non pas une classe, mais un type primitif. Il faut se reposer encore une fois sur l'autoboxing.
Inversement si le type de retour est primitif, la méthode invoke() renverra plutôt le type enveloppe. Il faudra donc transtyper en conséquence comme cela vous est montré dans l'exemple ci-dessus.
Comment obtenir un objet Method ? Il est bien évidemment possible d'appeler getDeclaredMethods(), qui renvoie un tableau d'objets Method dans lequel nous rechercherons ensuite la méthode désirée. Nous pouvons également, comme dans l'exemple ci-dessus, appeler la méthode getDeclaredMethod(), ou getMethod(), de la classe Class. Elle est comparable à getField(), qui reçoit une chaîne de caractères représentant un nom d'attribut et renvoie un objet Field.
Cependant, puisqu'il peut exister plusieurs méthodes homonymes, il faut être certain d'obtenir la bonne. C'est la raison pour laquelle, il faut également fournir les types de paramètres de la méthode désirée. La signature de getMethod(), ou getDeclaredMethod(), est :
Method Class.getMethod(String nom, Class... typeParamètres) ;
init:
deps-jar:
Compiling 1 source file to C:\netbeans-5.0\travail\Test\build\classes
compile:
run:
Nom de la méthode ?
cos
x ?
0
cos(0.0) = 1.0
BUILD SUCCESSFUL (total time: 26 seconds)
Maintenant que nous connaissons les règles d'utilisation des objets Method, nous allons mettre en oeuvre un tout petit programme qui permet d'exécuter la fonction mathématique décidée par l'opérateur :
package test; import java.lang.reflect.*; import java.util.Scanner; import static java.lang.System.*; public class Main { public static void main(String[] args) throws Exception { Scanner clavier = new Scanner(in); out.println("Nom de la méthode ? "); String nomMéthode = clavier.next(); out.println("x ? "); double x = clavier.nextDouble(); Method méthode = Math.class.getMethod(nomMéthode, double.class); out.println(nomMéthode+"("+x+") = "+méthode.invoke(null, x)); } }
Les paramètres et le résultat de la méthode invoke() sont nécessairement de type Object. Cela signifie que nous devons effectuer un bon nombre de transtypages. En conséquence, le compilateur n'a pas l'occasion de vérifier votre code, et les erreurs n'apparaissent que durant les tests, lorsqu'elles sont plus difficiles à corriger.
Comme nous l'avons déjà découvert au travers d'un exemple, il existe une autre méthode utile qui permet de créer une instance de classe à la volée. Cette méthode se nomme, assez naturellement newInstance(). Par exemple :
classe.getClass().newInstance();
Crée une nouvelle instance (objet) du même type de classe que classe. La méthode newInstance() appelle le constructeur par défaut pour initialiser l'objet nouvellement créé. Une exception est déclenchée si la classe ne dispose pas de constructeur par défaut.
Une combinaison de forName() et de newInstance() permet de créer un objet à partir d'un nom de classe stockée dans une chaîne :
init:
deps-jar:
Compiling 1 source file to C:\netbeans-5.0\travail\Test\build\classes
compile:
run:
Nom de la classe : java.awt.Point
java.awt.Point[x=0,y=0]
BUILD SUCCESSFUL (total time: 3 seconds)
package test; import java.util.Scanner; import static java.lang.System.*; public class Main { public static void main(String[] args) throws Exception { Scanner clavier = new Scanner(in); out.print("Nom de la classe : "); String nomClasse = clavier.next(); Object objet = Class.forName(nomClasse).newInstance(); out.println(objet); } }
Vous ne pourrez pas employer cette technique si vous devez fournir des paramètres au constructeur d'une classe que vous souhaitez instancier à la volée. Vous devrez alors recourir à la méthode newInstance() de la classe Constructor.
init:
deps-jar:
Compiling 1 source file to C:\netbeans-5.0\travail\Test\build\classes
compile:
run:
java.awt.Point[x=20,y=50]
BUILD SUCCESSFUL (total time: 3 seconds)
La classe java.lang.reflect.Conctructor représente un constructeur d'objet qui accepte des arguments. Vous pouvez l'utiliser, selon le gestionnaire de sécurité, pour créer une nouvelle instance d'un objet. Pour cela, il faut donc appeler la méthode newInstance() de la classe Constructor en précisant les arguments voulus.
Object Constructor.newInstance(Object... arguments) ;
Au préalable, pour pouvoir utiliser cette méthode, il est bien sûr nécessaire de créer un objet Constructor, et ceci au moyen de la méthode getConstructor() de la classe Class :
Constructor Class.getConstructor(Class... types)
Vous avez ci-dessous un exemple très simple qui permet de voir comment construire un objet Point pourvu de deux arguments en utilisant le mécanisme de la réflexion :
import java.lang.reflect.*; import java.awt.*; public class Main { public static void main(String[] args) throws Exception { Constructor constructeur = Point.class.getConstructor(int.class, int.class); Object objet = constructeur.newInstance(20, 50); System.out.println(objet); } }
Les choses se passent comme dans une invocation de méthode ; il est vrai qu'un constructeur n'est rien d'autre qu'une méthode dotée de propriétés particulières. Au moment de la création du type Constructor, vous devez spécifier le nombre de paramètres qui composera le constructeur en précisant leur type respectif.
Tout ceci se fait au moyen de la méthode getConstructor() de la classe Class. Une fois que vous avez obtenu l'objet Constructor, vous pouvez créer une instance correspondante au moyen de la méthode newInstance() en spécifiant cette fois-ci la valeur des arguments.
Depuis la JDK 5.0, la classe Class est maintenant générique. A titre d'exemple, String.class est en fait un objet de la classe Class<String>. Le paramètre de type est utile car il permet aux méthodes de Class<T> d'être plus spécifiques sur leur type de retour. Les méthodes suivantes profitent du paramètre de type :
T newInstance()
T cast(Object obj)
T[] getEnumsConstants()
Class<? super T> getSuperclass()
Constructor<T> getConstructor(Class... typeParamètres)
Constructor<T> getDeclaredConstructor(Class... typeParamètres)