Guide de Java Reflection

Guide de réflexion Java

1. Vue d'ensemble

Dans cet article, nous allons explorer la réflexion Java, ce qui nous permet d'inspecter et / ou de modifier les attributs d'exécution des classes, interfaces, champs et méthodes. Cela est particulièrement utile lorsque nous ne connaissons pas leurs noms au moment de la compilation.

De plus, nous pouvons instancier de nouveaux objets, invoquer des méthodes et obtenir ou définir des valeurs de champs à l'aide de la réflexion.

2. Configuration du projet

To use java reflection, we do not need to include any special jars, toute configuration spéciale ou dépendances Maven. Le JDK est livré avec un groupe de classes qui sont regroupées dans le packagejava.lang.reflect spécifiquement à cet effet.

Il suffit donc d’importer les éléments suivants dans notre code:

import java.lang.reflect.*;

et nous sommes prêts à partir.

Pour accéder aux informations de classe, de méthode et de champ d'une instance, nous appelons la méthodegetClass qui renvoie la représentation de classe d'exécution de l'objet. L'objetclass retourné fournit des méthodes pour accéder aux informations sur une classe.

3. Exemple simple

Pour nous familiariser avec les choses, nous allons examiner un exemple très basique qui inspecte les champs d'un simple objet Java au moment de l'exécution.

Créons une simple classePerson avec uniquement les champsname etage et aucune méthode du tout. Voici la classe de personne:

public class Person {
    private String name;
    private int age;
}

Nous allons maintenant utiliser la réflexion Java pour découvrir les noms de tous les champs de cette classe. Pour apprécier le pouvoir de réflexion, nous allons construire un objetPerson et utiliser Object comme type de référence:

@Test
public void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() {
    Object person = new Person();
    Field[] fields = person.getClass().getDeclaredFields();

    List actualFieldNames = getFieldNames(fields);

    assertTrue(Arrays.asList("name", "age")
      .containsAll(actualFieldNames));
}

Ce test nous montre que nous sommes capables d'obtenir un tableau d'objetsField à partir de notre objetperson, même si la référence à l'objet est un type parent de cet objet.

Dans l'exemple ci-dessus, nous nous sommes intéressés uniquement aux noms de ces champs, mais il reste encore beaucoup à faire. Nous en verrons d'autres exemples dans les sections suivantes.

Remarquez comment nous utilisons une méthode d'assistance pour extraire les noms de champs réels, c'est un code très basique:

private static List getFieldNames(Field[] fields) {
    List fieldNames = new ArrayList<>();
    for (Field field : fields)
      fieldNames.add(field.getName());
    return fieldNames;
}

4. Cas d'utilisation de Java Reflection

Avant de passer aux différentes fonctionnalités de la réflexion Java, nous allons discuter de certaines des utilisations courantes que nous pouvons en trouver. La réflexion en Java est extrêmement puissante et peut s'avérer très utile à bien des égards.

Par exemple, dans de nombreux cas, nous avons une convention de nommage pour les tables de base de données. Nous pouvons choisir d'ajouter de la cohérence en pré-fixant nos noms de table avectbl_, de telle sorte qu'une table avec des données d'élève s'appelletbl_student_data.

Dans de tels cas, nous pouvons nommer l'objet java contenant les données de l'étudiant commeStudent ouStudentData. Ensuite, en utilisant le paradigme CRUD, nous avons un point d'entrée pour chaque opération de sorte que les opérationsCreate ne reçoivent qu'un ParamètreObject.

Nous utilisons ensuite la réflexion pour récupérer le nom de l'objet et les noms de champs. À ce stade, nous pouvons mapper ces données sur une table de base de données et affecter les valeurs de champ d'objet aux noms de champ de base de données appropriés.

5. Inspection des classes Java

Dans cette section, nous allons explorer le composant le plus fondamental de l’API de réflexion Java. Comme nous l'avons mentionné précédemment, les objets de classe Java nous donnent accès aux détails internes de tout objet.

Nous allons examiner les détails internes tels que le nom de classe d'un objet, leurs modificateurs, champs, méthodes, interfaces implémentées, etc.

5.1. Se préparer

Pour bien maîtriser l'API de réflexion, appliquée aux classes Java et avoir des exemples variés, nous allons créer une classe abstraiteAnimal qui implémente l'interfaceEating. Cette interface définit le comportement alimentaire de tout objetAnimal concret que nous créons.

Alors tout d'abord, voici l'interfaceEating:

public interface Eating {
    String eats();
}

puis l'implémentation concrèteAnimal de l'interfaceEating:

public abstract class Animal implements Eating {

    public static String CATEGORY = "domestic";
    private String name;

    protected abstract String getSound();

    // constructor, standard getters and setters omitted
}

Créons également une autre interface appeléeLocomotion qui décrit comment un animal se déplace:

public interface Locomotion {
    String getLocomotion();
}

Nous allons maintenant créer une classe concrète appeléeGoat qui étendAnimal et implémenteLocomotion. Puisque la super classe implémenteEating,Goat devra également implémenter les méthodes de cette interface:

public class Goat extends Animal implements Locomotion {

    @Override
    protected String getSound() {
        return "bleat";
    }

    @Override
    public String getLocomotion() {
        return "walks";
    }

    @Override
    public String eats() {
        return "grass";
    }

    // constructor omitted
}

À partir de ce moment, nous allons utiliser la réflexion Java pour examiner les aspects des objets Java qui apparaissent dans les classes et les interfaces ci-dessus.

5.2. Noms de classe

Commençons par obtenir le nom d'un objet à partir desClass:

@Test
public void givenObject_whenGetsClassName_thenCorrect() {
    Object goat = new Goat("goat");
    Class clazz = goat.getClass();

    assertEquals("Goat", clazz.getSimpleName());
    assertEquals("com.example.reflection.Goat", clazz.getName());
    assertEquals("com.example.reflection.Goat", clazz.getCanonicalName());
}

Notez que la méthodegetSimpleName deClass renvoie le nom de base de l’objet tel qu’il apparaîtrait dans sa déclaration. Ensuite, les deux autres méthodes renvoient le nom de classe complet, y compris la déclaration du package.

Voyons également comment nous pouvons créer un objet de la classeGoat si nous ne connaissons que son nom de classe complet:

@Test
public void givenClassName_whenCreatesObject_thenCorrect(){
    Class clazz = Class.forName("com.example.reflection.Goat");

    assertEquals("Goat", clazz.getSimpleName());
    assertEquals("com.example.reflection.Goat", clazz.getName());
    assertEquals("com.example.reflection.Goat", clazz.getCanonicalName());
}

Notez que le nom que nous passons à la méthode statiqueforName doit inclure les informations du package sinon nous obtiendrons unClassNotFoundException.

5.3. Modificateurs de classe

Nous pouvons déterminer les modificateurs utilisés sur une classe en appelant la méthodegetModifiers qui renvoie unInteger.. Chaque modificateur est un bit de drapeau qui est soit activé, soit effacé.

La classejava.lang.reflect.Modifier propose des méthodes statiques qui analysent lesInteger renvoyés pour la présence ou l'absence d'un modificateur spécifique.

Confirmons les modificateurs de certaines des classes que nous avons définies ci-dessus:

@Test
public void givenClass_whenRecognisesModifiers_thenCorrect() {
    Class goatClass = Class.forName("com.example.reflection.Goat");
    Class animalClass = Class.forName("com.example.reflection.Animal");

    int goatMods = goatClass.getModifiers();
    int animalMods = animalClass.getModifiers();

    assertTrue(Modifier.isPublic(goatMods));
    assertTrue(Modifier.isAbstract(animalMods));
    assertTrue(Modifier.isPublic(animalMods));
}

Nous sommes en mesure d'inspecter les modificateurs de n'importe quelle classe située dans un fichier jar de bibliothèque que nous importons dans notre projet.

Dans la plupart des cas, nous pouvons avoir besoin d'utiliser l'approcheforName plutôt que l'instanciation complète, car ce serait un processus coûteux dans le cas de classes lourdes en mémoire.

5.4. Information sur le paquet

En utilisant la réflexion Java, nous pouvons également obtenir des informations sur le paquet de n'importe quelle classe ou objet. Ces données sont regroupées dans la classePackage qui est renvoyée par un appel à la méthodegetPackage sur l'objet de classe.

Permet de lancer un test pour récupérer le nom du paquet:

@Test
public void givenClass_whenGetsPackageInfo_thenCorrect() {
    Goat goat = new Goat("goat");
    Class goatClass = goat.getClass();
    Package pkg = goatClass.getPackage();

    assertEquals("com.example.reflection", pkg.getName());
}

5.5. Super classe

Nous pouvons également obtenir la super classe de n'importe quelle classe java en utilisant la réflexion java.

Dans de nombreux cas, en particulier lors de l’utilisation des classes de bibliothèque ou des classes intégrées de java, nous ne connaissons peut-être pas à l’avance la super classe d’un objet que nous utilisons, cette sous-section vous montrera comment obtenir ces informations.

Alors allons-y et déterminons la super classe deGoat, en plus, nous montrons également que la classejava.lang.String est une sous-classe de la classejava.lang.Object:

@Test
public void givenClass_whenGetsSuperClass_thenCorrect() {
    Goat goat = new Goat("goat");
    String str = "any string";

    Class goatClass = goat.getClass();
    Class goatSuperClass = goatClass.getSuperclass();

    assertEquals("Animal", goatSuperClass.getSimpleName());
    assertEquals("Object", str.getClass().getSuperclass().getSimpleName());
}

5.6. Interfaces implémentées

En utilisant la réflexion java, nous sommes également capables deget the list of interfaces implemented by a given class.

Récupérons les types de classe des interfaces implémentées par la classeGoat et la classe abstraiteAnimal:

@Test
public void givenClass_whenGetsImplementedInterfaces_thenCorrect(){
    Class goatClass = Class.forName("com.example.reflection.Goat");
    Class animalClass = Class.forName("com.example.reflection.Animal");

    Class[] goatInterfaces = goatClass.getInterfaces();
    Class[] animalInterfaces = animalClass.getInterfaces();

    assertEquals(1, goatInterfaces.length);
    assertEquals(1, animalInterfaces.length);
    assertEquals("Locomotion", goatInterfaces[0].getSimpleName());
    assertEquals("Eating", animalInterfaces[0].getSimpleName());
}

Notez à partir des assertions que chaque classe implémente une seule interface. En inspectant les noms de ces interfaces, nous constatons queGoat implémenteLocomotion etAnimal implémenteEating, comme cela apparaît dans notre code.

Vous avez peut-être remarqué queGoat est une sous-classe de la classe abstraiteAnimal et implémente la méthode d'interfaceeats(), puisGoat implémente également l'interfaceEating.

Il est donc intéressant de noter que seules les interfaces qu'une classe déclare explicitement comme implémentées avec le mot cléimplements apparaissent dans le tableau retourné.

Ainsi, même si une classe implémente des méthodes d’interface parce que sa super classe implémente cette interface, mais que la sous-classe ne déclare pas directement cette interface avec le mot cléimplements, alors cette interface n’apparaîtra pas dans le tableau des interfaces.

5.7. Constructeurs, méthodes et champs

Avec la réflexion java, nous sommes en mesure d’inspecter les constructeurs de toute classe d’objet ainsi que les méthodes et les champs.

Nous pourrons plus tard voir des inspections plus approfondies sur chacun de ces composants d’une classe, mais pour le moment, il suffit d’obtenir leur nom et de les comparer avec ce que nous attendons.

Voyons comment obtenir le constructeur de la classeGoat:

@Test
public void givenClass_whenGetsConstructor_thenCorrect(){
    Class goatClass = Class.forName("com.example.reflection.Goat");

    Constructor[] constructors = goatClass.getConstructors();

    assertEquals(1, constructors.length);
    assertEquals("com.example.reflection.Goat", constructors[0].getName());
}

Nous pouvons également inspecter les champs de la classeAnimal comme ceci:

@Test
public void givenClass_whenGetsFields_thenCorrect(){
    Class animalClass = Class.forName("com.example.java.reflection.Animal");
    Field[] fields = animalClass.getDeclaredFields();

    List actualFields = getFieldNames(fields);

    assertEquals(2, actualFields.size());
    assertTrue(actualFields.containsAll(Arrays.asList("name", "CATEGORY")));
}

Tout comme nous pouvons inspecter les méthodes de la classeAnimal:

@Test
public void givenClass_whenGetsMethods_thenCorrect(){
    Class animalClass = Class.forName("com.example.java.reflection.Animal");
    Method[] methods = animalClass.getDeclaredMethods();
    List actualMethods = getMethodNames(methods);

    assertEquals(4, actualMethods.size());
    assertTrue(actualMethods.containsAll(Arrays.asList("getName",
      "setName", "getSound")));
}

Tout commegetFieldNames, nous avons ajouté une méthode d'assistance pour récupérer les noms de méthode à partir d'un tableau d'objetsMethod:

private static List getMethodNames(Method[] methods) {
    List methodNames = new ArrayList<>();
    for (Method method : methods)
      methodNames.add(method.getName());
    return methodNames;
}

6. Inspection des constructeurs

Avec la réflexion java, nous pouvonsinspect constructors de n'importe quelle classe et mêmecreate class objects at runtime. Ceci est rendu possible par la classejava.lang.reflect.Constructor.

Plus tôt, nous avons seulement regardé comment obtenir le tableau des objetsConstructor, à partir duquel nous avons pu obtenir les noms des constructeurs.

Dans cette section, nous allons nous concentrer sur la façon de récupérer des constructeurs spécifiques. Comme nous le savons, en Java, deux constructeurs d'une classe ne partagent pas exactement la même signature de méthode. Nous allons donc utiliser cette unicité pour obtenir un constructeur parmi plusieurs.

Pour apprécier les fonctionnalités de cette classe, nous allons créer une sous-classeBird deAnimal avec trois constructeurs. Nous n'implémenterons pasLocomotion afin de pouvoir spécifier ce comportement en utilisant un argument constructeur, pour ajouter encore plus de variété:

public class Bird extends Animal {
    private boolean walks;

    public Bird() {
        super("bird");
    }

    public Bird(String name, boolean walks) {
        super(name);
        setWalks(walks);
    }

    public Bird(String name) {
        super(name);
    }

    public boolean walks() {
        return walks;
    }

    // standard setters and overridden methods
}

Confirmons par réflexion que cette classe a trois constructeurs:

@Test
public void givenClass_whenGetsAllConstructors_thenCorrect() {
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Constructor[] constructors = birdClass.getConstructors();

    assertEquals(3, constructors.length);
}

Ensuite, nous récupérerons chaque constructeur pour la classeBird en passant les types de classe de paramètres du constructeur dans l'ordre déclaré:

@Test
public void givenClass_whenGetsEachConstructorByParamTypes_thenCorrect(){
    Class birdClass = Class.forName("com.example.reflection.Bird");

    Constructor cons1 = birdClass.getConstructor();
    Constructor cons2 = birdClass.getConstructor(String.class);
    Constructor cons3 = birdClass.getConstructor(String.class, boolean.class);
}

Il n'y a pas besoin d'assertion car lorsqu'un constructeur avec des types de paramètres donnés dans l'ordre donné n'existe pas, nous obtiendrons unNoSuchMethodException et le test échouera automatiquement.

Dans le dernier test, nous verrons comment instancier des objets à l'exécution tout en fournissant leurs paramètres:

@Test
public void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() {
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Constructor cons1 = birdClass.getConstructor();
    Constructor cons2 = birdClass.getConstructor(String.class);
    Constructor cons3 = birdClass.getConstructor(String.class,
      boolean.class);

    Bird bird1 = (Bird) cons1.newInstance();
    Bird bird2 = (Bird) cons2.newInstance("Weaver bird");
    Bird bird3 = (Bird) cons3.newInstance("dove", true);

    assertEquals("bird", bird1.getName());
    assertEquals("Weaver bird", bird2.getName());
    assertEquals("dove", bird3.getName());

    assertFalse(bird1.walks());
    assertTrue(bird3.walks());
}

Nous instancions les objets de classe en appelant la méthodenewInstance de la classeConstructor et en passant les paramètres requis dans l'ordre déclaré. Nous convertissons ensuite le résultat dans le type requis.

Pourbird1, nous utilisons le constructeur par défaut qui, à partir de notre codeBird, définit automatiquement le nom sur bird et nous le confirmons avec un test.

Nous instancions ensuitebird2 avec seulement un nom et un test également, rappelez-vous que lorsque nous ne définissons pas le comportement de locomotion comme il est par défaut faux, comme on le voit dans les deux dernières assertions.

7. Inspection des champs

Auparavant, nous n'inspections que les noms des champs, dans cette section,we will show how toget and set their values at runtime.

Il existe deux méthodes principales utilisées pour inspecter les champs d'une classe lors de l'exécution:getFields() etgetField(fieldName).

La méthodegetFields() renvoie tous les champs publics accessibles de la classe en question. Il renverra tous les champs publics de la classe et de toutes les super classes.

Par exemple, lorsque nous appelons cette méthode sur la classeBird, nous n'obtiendrons que le champCATEGORY de sa super classe,Animal, puisqueBird lui-même ne déclare aucun champs publics:

@Test
public void givenClass_whenGetsPublicFields_thenCorrect() {
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Field[] fields = birdClass.getFields();

    assertEquals(1, fields.length);
    assertEquals("CATEGORY", fields[0].getName());
}

Cette méthode a également une variante appeléegetField qui ne retourne qu'un seul objetField en prenant le nom du champ:

@Test
public void givenClass_whenGetsPublicFieldByName_thenCorrect() {
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Field field = birdClass.getField("CATEGORY");

    assertEquals("CATEGORY", field.getName());
}

Nous ne pouvons pas accéder aux champs privés déclarés dans les super classes et non déclarés dans la classe enfant. C'est pourquoi nous ne pouvons pas accéder au champname.

Cependant, nous pouvons inspecter les champs privés déclarés dans la classe que nous traitons en appelant la méthodegetDeclaredFields:

@Test
public void givenClass_whenGetsDeclaredFields_thenCorrect(){
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Field[] fields = birdClass.getDeclaredFields();

    assertEquals(1, fields.length);
    assertEquals("walks", fields[0].getName());
}

Nous pouvons également utiliser son autre variante si nous connaissons le nom du champ:

@Test
public void givenClass_whenGetsFieldsByName_thenCorrect() {
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Field field = birdClass.getDeclaredField("walks");

    assertEquals("walks", field.getName());
}

Si nous nous trompons sur le nom du champ ou si nous saisissons un champ inexistant, nous obtiendrons unNoSuchFieldException.

Nous obtenons le type de champ comme suit:

@Test
public void givenClassField_whenGetsType_thenCorrect() {
    Field field = Class.forName("com.example.reflection.Bird")
      .getDeclaredField("walks");
    Class fieldClass = field.getType();

    assertEquals("boolean", fieldClass.getSimpleName());
}

Nous verrons ensuite comment accéder aux valeurs de champ et les modifier. Pour pouvoir obtenir la valeur d'un champ, et encore moins le définir, nous devons d'abord le définir comme accessible en appelant la méthodesetAccessible sur l'objetField et lui passer le booléentrue:

@Test
public void givenClassField_whenSetsAndGetsValue_thenCorrect() {
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Bird bird = (Bird) birdClass.newInstance();
    Field field = birdClass.getDeclaredField("walks");
    field.setAccessible(true);

    assertFalse(field.getBoolean(bird));
    assertFalse(bird.walks());

    field.set(bird, true);

    assertTrue(field.getBoolean(bird));
    assertTrue(bird.walks());
}

Dans le test ci-dessus, nous vérifions qu'effectivement la valeur du champwalks est fausse avant de le définir sur vrai.

Remarquez comment nous utilisons l'objetField pour définir et obtenir des valeurs en lui passant l'instance de la classe avec laquelle nous traitons et éventuellement la nouvelle valeur que nous voulons que le champ ait dans cet objet.

Une chose importante à noter à propos des objetsField est que quand il est déclaré commepublic static, alors nous n'avons pas besoin d'une instance de la classe les contenant, nous pouvons simplement passernull dans son placez et obtenez toujours la valeur par défaut du champ, comme ceci:

@Test
public void givenClassField_whenGetsAndSetsWithNull_thenCorrect(){
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Field field = birdClass.getField("CATEGORY");
    field.setAccessible(true);

    assertEquals("domestic", field.get(null));
}

8. Inspection des méthodes

Dans un exemple précédent, nous utilisions la réflexion uniquement pour inspecter les noms de méthodes. Cependant, la réflexion Java est plus puissante que cela.

Avec la réflexion java, nous pouvonsinvoke methods atruntime et leur transmettre les paramètres requis, comme nous l'avons fait pour les constructeurs. De même, nous pouvons également invoquer des méthodes surchargées en spécifiant les types de paramètres de chacune.

Tout comme les champs, nous utilisons deux méthodes principales pour récupérer les méthodes de classe. La méthodegetMethods renvoie un tableau de toutes les méthodes publiques de la classe et des super classes.

Cela signifie qu'avec cette méthode, nous pouvons obtenir des méthodes publiques de la classejava.lang.Object commetoString, hashCode etnotifyAll:

@Test
public void givenClass_whenGetsAllPublicMethods_thenCorrect(){
    Class birdClass = Class.forName("com.example.java.reflection.Bird");
    Method[] methods = birdClass.getMethods();
    List methodNames = getMethodNames(methods);

    assertTrue(methodNames.containsAll(Arrays
      .asList("equals", "notifyAll", "hashCode",
        "walks", "eats", "toString")));
}

Pour obtenir uniquement les méthodes publiques de la classe qui nous intéresse, nous devons utiliser la méthodegetDeclaredMethods:

@Test
public void givenClass_whenGetsOnlyDeclaredMethods_thenCorrect(){
    Class birdClass = Class.forName("com.example.java.reflection.Bird");
    List actualMethodNames
      = getMethodNames(birdClass.getDeclaredMethods());

    List expectedMethodNames = Arrays
      .asList("setWalks", "walks", "getSound", "eats");

    assertEquals(expectedMethodNames.size(), actualMethodNames.size());
    assertTrue(expectedMethodNames.containsAll(actualMethodNames));
    assertTrue(actualMethodNames.containsAll(expectedMethodNames));
}

Chacune de ces méthodes a la variation singulière qui renvoie un seul objetMethod dont nous connaissons le nom:

@Test
public void givenMethodName_whenGetsMethod_thenCorrect() {
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Method walksMethod = birdClass.getDeclaredMethod("walks");
    Method setWalksMethod = birdClass.getDeclaredMethod("setWalks", boolean.class);

    assertFalse(walksMethod.isAccessible());
    assertFalse(setWalksMethod.isAccessible());

    walksMethod.setAccessible(true);
    setWalksMethod.setAccessible(true);

    assertTrue(walksMethod.isAccessible());
    assertTrue(setWalksMethod.isAccessible());
}

Remarquez comment nous récupérons des méthodes individuelles et spécifions les types de paramètres qu’elles prennent. Celles qui ne prennent pas de type de paramètre sont récupérées avec un argument de variable vide, nous laissant avec un seul argument, le nom de la méthode.

Ensuite, nous montrerons comment invoquer une méthode à l'exécution. Nous savons par défaut que l'attributwalks de la classeBird estfalse, nous voulons appeler sa méthodesetWalks et la mettre àtrue:

@Test
public void givenMethod_whenInvokes_thenCorrect() {
    Class birdClass = Class.forName("com.example.reflection.Bird");
    Bird bird = (Bird) birdClass.newInstance();
    Method setWalksMethod = birdClass.getDeclaredMethod("setWalks", boolean.class);
    Method walksMethod = birdClass.getDeclaredMethod("walks");
    boolean walks = (boolean) walksMethod.invoke(bird);

    assertFalse(walks);
    assertFalse(bird.walks());

    setWalksMethod.invoke(bird, true);

    boolean walks2 = (boolean) walksMethod.invoke(bird);
    assertTrue(walks2);
    assertTrue(bird.walks());
}

Remarquez comment nous appelons d'abord la méthodewalks et convertissons le type de retour dans le type de données approprié, puis vérifions sa valeur. Nous invoquons ensuite plus tard la méthodesetWalks pour changer cette valeur et tester à nouveau.

9. Conclusion

Dans ce didacticiel, nous avons présenté l'API Java Reflection et expliqué comment l'utiliser pour inspecter des classes, des interfaces, des champs et des méthodes au moment de l'exécution sans connaissance préalable de leurs composants internes au moment de la compilation.

Le code source complet et les exemples de ce didacticiel se trouvent dans mon projetGithub.