1. Qu’est‑ce que la réflexion
La réflexion en Java est un mécanisme qui permet à un programme d’inspecter, et même de modifier, sa propre structure à l’exécution. C’est comme si vous pouviez regarder à l’intérieur de vous‑même et découvrir : « Quels champs et méthodes ai‑je ? Quel est mon constructeur ? De quel type suis‑je exactement ? » — et même invoquer une méthode privée sur vous‑même.
Définition formelle :
La réflexion est une API permettant d’obtenir des informations sur les classes, interfaces, champs, méthodes et constructeurs au moment de l’exécution du programme, ainsi que d’interagir avec eux.
À quoi sert la réflexion ?
- Frameworks : Spring, Hibernate, JUnit utilisent la réflexion pour leur « magie » : création automatique d’objets, injection de dépendances, appel de méthodes par nom et en fonction des annotations.
- Sérialisation : Transformation d’objets en JSON/XML et inversement — il faut connaître les champs et pouvoir y accéder.
- Tests : Recherche et invocation automatiques des méthodes annotées (par exemple, @Test).
- Chargement dynamique de classes : Chargement d’une classe par son nom à l’exécution (plugins, pilotes).
- IDE et analyse de code : Complétion automatique, inspections, refactorisation.
Exemple concret
public class Person {
private String name;
private int age;
public void sayHello() {
System.out.println("Bonjour, je m'appelle " + name);
}
}
Avec la réflexion, nous pouvons, à l’exécution, découvrir que la classe Person possède les champs name et age, ainsi que la méthode sayHello. De plus, on peut modifier la valeur d’un champ privé ou appeler une méthode par son nom.
Un peu d’humour
On peut dire que la réflexion, c’est comme dans « Matrix » : vous réalisez que tout votre code est une donnée qui peut être inspectée et modifiée à la volée. N’oubliez pas la pilule rouge de la sécurité !
2. La classe Class : le cœur de la réflexion
En Java, chaque objet et chaque type à l’exécution est associé à un objet particulier de type Class. Cet objet constitue la « méta‑information » sur le type. La classe java.lang.Class<T> est le point d’entrée central dans le monde de la réflexion.
Comment obtenir un objet Class ?
Il existe plusieurs façons :
- Via .class
La manière la plus directe et sûre si la classe est connue à la compilation :Class<Person> personClass = Person.class; - À partir d’un objet : getClass()
Si vous avez déjà une instance :
Ici, clazz est l’objet Class qui décrit la classe réelle de l’objet p.Person p = new Person(); Class<?> clazz = p.getClass(); - À partir d’une chaîne : Class.forName()
Si le nom de la classe n’est connu qu’à l’exécution :
Convient pour charger des plugins, des pilotes et autres composants dynamiques.Class<?> clazz = Class.forName("com.example.Person");
Exemple : obtenir le nom de la classe
Person p = new Person();
Class<?> clazz = p.getClass();
System.out.println(clazz.getName()); // Affichera: Person ou com.example.Person
Tableau: principales façons d’obtenir un objet Class
| Comment l’obtenir | Quand l’utiliser | Exemple |
|---|---|---|
|
Si la classe est connue à la compilation | |
|
Si vous avez un objet | |
|
Si le nom de la classe est connu à l’exécution | |
Schéma: l’objet et son Class
+-------------------+
| Person p |
+-------------------+
|
v
+-------------------+
| p.getClass() |
+-------------------+
|
v
+-------------------+
| Class<Person> |
+-------------------+
Vérification du type via Class
Parfois, il faut savoir si un objet appartient à une classe précise :
if (p.getClass() == Person.class) {
System.out.println("C'est bien Person !");
}
Ou en tenant compte de l’héritage — l’opérateur habituel instanceof :
if (p instanceof Person) {
// fonctionne comme d'habitude
}
3. Quand la réflexion est utile — et quand elle ne l’est pas
Un grand pouvoir implique de grandes responsabilités. La réflexion est un outil puissant, mais il ne faut l’utiliser que lorsque c’est vraiment nécessaire.
Quand la réflexion est nécessaire
- Frameworks et bibliothèques : Spring, Hibernate, JUnit, Jackson — automatisation, « magie » et moins de code « manuel ».
- Sérialisation : Il faut connaître les champs de l’objet pour le transformer en JSON/XML.
- Plugins et extensions : Les classes sont chargées dynamiquement et leur structure est inconnue à l’avance.
Pourquoi il ne faut pas abuser de la réflexion
- Perte de sûreté de typage : Les erreurs apparaissent à l’exécution.
- Difficulté de maintenance : Le code devient moins évident.
- Performances : Plus lent que les appels directs.
- Sécurité : On peut contourner l’encapsulation et exposer des données privées.
Exemple : quand la réflexion est inutile
p.sayHello();
Exemple : quand la réflexion est indispensable
Vous écrivez un mini‑framework de test : l’utilisateur marque des méthodes avec l’annotation @Test, et le programme doit les trouver et les appeler automatiquement — impossible sans réflexion.
4. Démonstration : découverte de Class
Exemple : afficher le nom de la classe et sa superclasse
public class ReflectionDemo {
public static void main(String[] args) {
String s = "Hello, reflection!";
Class<?> clazz = s.getClass();
System.out.println("Nom de la classe : " + clazz.getName());
System.out.println("Nom simple de la classe : " + clazz.getSimpleName());
System.out.println("Package : " + clazz.getPackageName());
System.out.println("Superclasse : " + clazz.getSuperclass().getName());
}
}
Résultat :
Nom de la classe : java.lang.String
Nom simple de la classe : String
Package : java.lang
Superclasse : java.lang.Object
Exemple : obtenir Class pour les types primitifs
Class<Integer> intClass = int.class;
System.out.println(intClass.getName()); // int
Class<?> doubleClass = double.class;
System.out.println(doubleClass.getName()); // double
Exemple : obtenir Class pour un tableau
int[] arr = new int[10];
Class<?> arrClass = arr.getClass();
System.out.println(arrClass.getName()); // [I (format spécifique pour les tableaux)
Exemple : vérification de l’appartenance à un type
if (arrClass.isArray()) {
System.out.println("C'est un tableau !");
}
5. Pratique: mini‑programme « Quelle est cette classe ? »
Écrivons un programme qui prend le nom complet d’une classe (par exemple, "java.util.ArrayList") et affiche ses informations principales.
import java.util.Scanner;
public class ClassInfoPrinter {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("Saisissez le nom complet de la classe : ");
String className = scanner.nextLine();
Class<?> clazz = Class.forName(className);
System.out.println("Nom de la classe : " + clazz.getName());
System.out.println("Package : " + clazz.getPackageName());
System.out.println("Superclasse : " + clazz.getSuperclass().getName());
Class<?>[] interfaces = clazz.getInterfaces();
System.out.print("Implémente les interfaces : ");
for (Class<?> i : interfaces) {
System.out.print(i.getName() + " ");
}
System.out.println();
}
}
Exemple d’exécution :
Saisissez le nom complet de la classe : java.util.ArrayList
Nom de la classe : java.util.ArrayList
Package : java.util
Superclasse : java.util.AbstractList
Implémente les interfaces : java.util.List java.util.RandomAccess java.lang.Cloneable java.io.Serializable
Faits intéressants sur Class et la réflexion
- Chaque type chargé dans la JVM a un représentant unique — un objet Class. Toutes les instances d’un même type partagent le même Class<T>.
- On peut déterminer la nature du type : isInterface(), isEnum(), isArray(), isPrimitive(), etc.
- Si vous fournissez un nom incorrect à Class.forName, vous obtiendrez l’exception ClassNotFoundException.
- Avec la réflexion, on peut créer des objets sans connaître la classe à la compilation — nous en parlerons dans la prochaine leçon.
6. Erreurs typiques lors des premiers pas avec la réflexion
Erreur n°1 : penser que la réflexion est rapide.
En réalité, la réflexion est plus lente que les appels directs aux méthodes et l’accès aux champs. Ne l’utilisez pas pour chaque petite chose.
Erreur n°2 : tenter d’obtenir un Class pour une classe inexistante.
Si vous vous trompez de nom, vous obtiendrez une ClassNotFoundException. Gérez cette exception.
Erreur n°3 : confusion entre l’objet classe et l’instance.
L’objet Class décrit la structure du type, il n’en est pas une instance.
Erreur n°4 : utiliser la réflexion sans nécessité.
Si un code classique suffit — mieux vaut l’utiliser. La réflexion est utile pour la dynamique, les frameworks, les plugins et tâches similaires.
Erreur n°5 : manipulation imprudente des champs et méthodes privés.
La réflexion permet de contourner l’encapsulation, ce qui peut entraîner des erreurs et des problèmes de sécurité, surtout dans les grands projets.
GO TO FULL VERSION