La Réflexion en Java: Principes et Applications Techniques

La réflexion en Java est un mécanisme qui permet de charger dynamiquement une classe et d'examiner ses composants internes à l'exécution. Cette caractéristique confère à Java une flexibilité comparable aux langages dynamiques, ce qui est crucial pour la compréhension de frameworks comme Spring.

La classe Class représente le bytecode d'une classe et se trouve dans le package java.lang. Pour charger le bytecode, on utilise la méthode Class.forName("nom_complet_de_la_classe"), par exemple Class.forName("com.mysql.jdbc.Driver"), qui charge la classe en mémoire et encapsule l'objet Class correspondant.

Il existe plusieurs façons d'obtenir un objet Class. Voici un exemple avec une classe Exemple :

package org.reflexion.test;

import java.io.Serializable;

public class Exemple implements Serializable {
    public String nomExempleA;
    public String nomExempleB;
    private String typeExempleX;
    private String typeExempleY;

    public Exemple() {}
    public Exemple(String nom, String type) {}

    private void recupererNomA() {}
    public void afficherSalut(String message) {
        System.out.println("Salut public " + message);
    }
    private void afficherSalutPrive(String message) {
        System.out.println("Salut privé " + message);
    }
}

Obtention de l'objet Class :

// Méthode 1
Class classe1 = Class.forName("org.reflexion.test.Exemple");
// Méthode 2
Class classe2 = Exemple.class;
// Méthode 3
Class classe3 = new Exemple().getClass();

On peut ensuite extraire des informations sur la classe, comme le nom du package, le nom complet, les modificateurs, la classe parente, les interfaces et les constructeurs :

String paquet = classe1.getPackage().getName();
String nomComplet = classe1.getName();
int modificateurs = classe1.getModifiers();
System.out.println("Modificateurs : " + java.lang.reflect.Modifier.toString(modificateurs));

Class superClasse = classe1.getSuperclass();
System.out.println("Classe parente : " + superClasse);

Class[] interfaces = classe1.getInterfaces();
for (Class iface : interfaces) {
    System.out.println("Interface : " + iface.getName());
}

java.lang.reflect.Constructor[] constructeurs = classe1.getConstructors();
for (java.lang.reflect.Constructor cons : constructeurs) {
    System.out.println("Constructeur : " + cons.getName());
}

Pour les champs, deux méthodes clés existent : getFields() qui retourne les champs publics (y compris ceux hérités), et getDeclaredFields() qui retourne tous les champs déclarés dans la classe (public, protégé, par défaut, privé) mais exclut les champs hérités.

java.lang.reflect.Field[] champsPublics = classe1.getFields();
for (java.lang.reflect.Field champ : champsPublics) {
    System.out.println("Champ public : " + champ.getName());
}

java.lang.reflect.Field[] tousChamps = classe1.getDeclaredFields();
for (java.lang.reflect.Field champ : tousChamps) {
    System.out.println("Champ déclaré : " + champ.getName());
}

Pour les méthodes, getMethods() fournit les méthodes publiques de la classe, de ses parents et des interfaces, tandis que getDeclaredMethods() donne toutes les méthodes définies dans la classe courante, y compris les privées.

java.lang.reflect.Method[] methodesPubliques = classe1.getMethods();
for (java.lang.reflect.Method methode : methodesPubliques) {
    System.out.println("Méthode publique : " + methode.getName());
}

java.lang.reflect.Method[] methodesDeclarees = classe1.getDeclaredMethods();
for (java.lang.reflect.Method methode : methodesDeclarees) {
    System.out.println("Méthode déclarée : " + methode.getName());
}

Pour invoquer une méthode via la réflexion, on crée une instance de la classe, on obtient l'objet Method correspondant, et on utilise invoke(). Les méthodes privées peuvent être accessibles en appelant setAccessible(true).

Exemple instance = (Exemple) classe2.newInstance();
java.lang.reflect.Method methodePublique = classe2.getMethod("afficherSalut", String.class);
methodePublique.invoke(instance, "Monde!!!");

Exemple instancePrivee = (Exemple) classe1.newInstance();
java.lang.reflect.Method methodePrivee = classe1.getDeclaredMethod("afficherSalutPrive", String.class);
methodePrivee.setAccessible(true);
methodePrivee.invoke(instancePrivee, "Monde!!!!");

Cette approche montre que la réflexion permet d'accéder à des méthodes privées d'autres classes, contournant ainsi les restrictions normales de visibilité en Java.

Étiquettes: Java réflexion Introspection Spring Programmation orientée objet

Publié le 9 juin à 01h33