# Programmation Java/Réflexion

* Introduction
* La notion de machine virtuelle
* Installation
** NetBeans
** Eclipse
** Visual Studio Code
* Bases du langage
** Premier programme
** Commentaires
** Types de base
** Opérateurs
** Conditions
** Bloc de portée
** Itérations
** Tableaux
* Les classes en Java
** Membres
** Modificateurs d'accès
** Héritage
** Encapsulation
** Polymorphisme
** Classes abstraites
** Interfaces
** Classes internes
** Transtypage
** Types génériques
** Instanciation et cycle de vie
** « this »
** Objets comparables et clés
** Énumérations
** Exceptions
** Packages
* Classes de base
** Collections
** Nombre de taille arbitraire
** Tags Javadoc
** Annotations
** Entrées sorties
** Threads et synchronisation
** Méthodes natives
** Réflexion
** Regex
** JUnit
* Conclusion
* Exemples en Java
** Générer un carré magique
** Générer un triangle de Sierpiński
** XML
** Swing
*** Un premier exemple
*** Formulaire avec boutons sur image
*** Calcul de somme
*** Calcul de distance
* Annexes
** Les outils de base fournis avec le Java Development Kit
** Créer une archive Java
** Paramétrer la JVM
** Liste des mots réservés
** Débogage
** Java et les autres langages
* Introduction
* La notion de machine virtuelle
* Installation
** NetBeans
** Eclipse
** Visual Studio Code
* Bases du langage
** Premier programme
** Commentaires
** Types de base
** Opérateurs
** Conditions
** Bloc de portée
** Itérations
** Tableaux
* Les classes en Java
** Membres
** Modificateurs d'accès
** Héritage
** Encapsulation
** Polymorphisme
** Classes abstraites
** Interfaces
** Classes internes
** Transtypage
** Types génériques
** Instanciation et cycle de vie
** « this »
** Objets comparables et clés
** Énumérations
** Exceptions
** Packages
* Classes de base
** Collections
** Nombre de taille arbitraire
** Tags Javadoc
** Annotations
** Entrées sorties
** Threads et synchronisation
** Méthodes natives
** Réflexion
** Regex
** JUnit
* Conclusion
* Exemples en Java
** Générer un carré magique
** Générer un triangle de Sierpiński
** XML
** Swing
*** Un premier exemple
*** Formulaire avec boutons sur image
*** Calcul de somme
*** Calcul de distance
* Annexes
** Les outils de base fournis avec le Java Development Kit
** Créer une archive Java
** Paramétrer la JVM
** Liste des mots réservés
** Débogage
** Java et les autres langages
[ Modifier le sommaire ]
La réflexion (reflection en anglais) permet l'introspection des classes, c'est-à-dire de charger une classe, d'en créer une instance et d'accéder aux membres statiques ou non (appel de méthodes, lire et écrire les attributs) sans connaître la classe par avance.
Java possède une API permettant la réflexion. Elle sert notamment à gérer des extensions (plug-in en anglais) pour une application.

## L'API de réflexion

Le package java.lang possède trois classes utilisées pour l'utilisation dynamique des classes :

### java.lang.Class

    Cette classe permet d'accéder aux caractéristiques d'une classe, à ses membres (méthodes et attributs), à la classe mère.

### java.lang.ClassLoader

    Cette classe permet de gérer le chargement de classe. Il existe des sous-classes dont notamment java.net.URLClassLoader permettant de charger des classes en les cherchant dans une liste d'URLs (donc de fichiers JAR et répertoires également, en convertissant le chemin du fichier ou répertoire en URL).

### java.lang.Package

    Cette classe permet d'accéder aux informations d'un package (informations de version, annotations, ...).
Les autres classes utiles sont définies dans le package java.lang.reflect et permettent d'accéder aux détails d'une classe. Les principales classes sont les suivantes :

### java.lang.reflect.Constructor

    Référence à un constructeur d'une classe.

### java.lang.reflect.Method

    Référence à une méthode d'une classe.

### java.lang.reflect.Field

    Référence à un champ d'une classe.

### java.lang.reflect.Modifier

    Attributs et méthodes statiques pour décoder les modificateurs des membres (public, private, protected, static, abstract, final, native, ...).
Les classes représentant des membres d'une classe (Constructor, Method, Field) implémentent toutes l'interface java.lang.reflect.Member comportant les méthodes suivantes :

### Class getDeclaringClass

    Retourne la classe définissant ce membre.

### String getName

    Retourne le nom.

### int getModifiers

    Retourne les modificateurs (public, protected, private, static, final, ...).

### boolean isSynthetic

    Teste si ce membre a été généré par le compilateur.

### Charger une classe dynamiquement

La classe java.lang.Class possède deux méthodes statiques pour obtenir une classe (après chargement si nécessaire) :

#### static Class forName(String name)

    Cette méthode équivaut à appeler la seconde méthode avec pour paramètres : (name, true, this.getClass.getClassLoader).

#### static Class forName(String name, boolean initialize, ClassLoader loader)

    Charge la classe dont le nom complet (incluant les packages) est spécifié, en utilisant l'instance du chargeur de classe fourni. Le paramètre initialize vaut true pour initialiser la classe (appeler le bloc d'initialisation statique), ou false pour ne pas l'initialiser.
Il est également possible d'obtenir une java.lang.Class de manière statique :
* à partir d'un objet en appelant la méthode getClass,
* à partir d'une référence à la classe en utilisant le champ class.
Exemple :

### Liste des membres d'une classe

Les méthodes suivantes permettent de lister les membres d'une classe :

#### getConstructors

    Cette méthode retourne un tableau de java.lang.reflect.Constructor contenant tous les constructeurs définis par la classe.

#### getMethods

    Cette méthode retourne un tableau de java.lang.reflect.Method contenant toutes les méthodes définies par la classe.

#### getFields

    Cette méthode retourne un tableau de java.lang.reflect.Field contenant tous les attributs définis dans la classe.
Les méthodes ci-dessus retournent les membres publics de la classe, comprenant également ceux hérités des classes mères. Il existe une variante "Declared" de ces méthodes retournant tous les membres (publics, protégés, privés) déclarés par la classe uniquement (les membres hérités sont exclus).
Au lieu de lister tous les membres, puis en rechercher un en particulier, il est possible d'utiliser les méthodes spécifiques de recherche d'un membre précis d'une classe (publics et hérités, ou bien "Declared" pour tous ceux déclarés par la classe seule) :

#### getDeclaredConstructor(Class... parameterTypes)

    Cette méthode retourne le constructeur déclaré avec les paramètres dont les types sont spécifiés.

#### getDeclaredMethod(String name, Class... parameterTypes)

    Cette méthode retourne la méthode portant de nom spécifié et déclarée avec les paramètres dont les types sont spécifiés.

#### getDeclaredField(String name)

    Cette méthode retourne l'attribut portant de nom spécifié.
Les membres retournés par toutes ces méthodes peuvent être d'instance ou statiques.
La méthode getAnnotations retourne un tableau de java.lang.Annotation contenant toutes les annotations associées à la classe.

### Instancier une classe et appel à un constructeur

La méthode newInstance de la classe java.lang.Class permet de créer une nouvelle instance de la classe, en appelant le constructeur sans paramètre de la classe (qui doit donc en posséder un) :
Une classe comme celle ci-dessous peut ne pas avoir de constructeur sans paramètres :
Dans ce cas, il faut d'abord obtenir le constructeur, puis l'appeler :
Pour les versions de Java antérieures à 5.0 où l'auto-boxing n'existe pas, et où il faut explicitement utiliser des tableaux :

### Appel à une méthode

L'appel à une méthode de la classe est basé sur le même principe que l'appel à un constructeur vu juste avant. Cependant, pour obtenir la référence à une méthode, il faut spécifier le nom. Lors de l'invocation de la méthode, il faut spécifier l'instance (l'objet) auquel s'applique la méthode (null pour une méthode statique).
Exemple :

### Accès à un attribut public

L'accès à un attribut public se fait en appelant les méthodes sur l'instance de java.lang.reflect.Field obtenu auprès de la classe.
Exemple :

### Accès à un attribut privé

Il est également possible d'avoir accès aux champs privés grâce à la méthode setAccessible de la classe Field. Cela est cependant fortement déconseillé puisque modifier les valeurs d'un champs privée revient à violer le principe d'encapsulation.
Exemple :

## Exemple concret : un gestionnaire d'extensions

L'extension d'une application Java peut se faire en utilisant la réflexion pour charger dynamiquement une classe. Le but de cet exemple est de permettre d'ajouter des fonctionnalités dynamiquement à une application, sans avoir à effectuer de changement autre que l'ajout d'une nouvelle archive JAR dans un répertoire.
Par défaut, une application est composée de plusieurs classes dont le chargement est effectué par la JVM. Cependant, il faut que les chemins des répertoires de fichiers *.class ou des archives *.jar soit renseignés d'avance avant le lancement de l'application dans le classpath.

### Chargeur de classes

Supposons que l'application doive être capable de charger des extensions fournies sous forme de fichiers *.jar situés dans un sous-répertoire nommé « ext ». Quand l'application est lancée, le classpath est déjà configuré pour les classes de l'application, mais ne contient pas le répertoire ext. Mais comme les classes ne sont pas directement dans le répertoire, la JVM a besoin du chemin de chaque fichier *.jar dans le classpath. L'application doit donc lister les fichiers *.jar du répertoire ext pour construire un classpath passé au constructeur d'un nouveau chargeur de classes.
Il est possible de créer un chargeur de classes par fichier *.jar. Cependant, chaque chargeur de classe a un chargeur de classe parent, permettant aux classes chargées d'utiliser des classes gérées par le chargeur parent. En l’occurrence il s'agit de celui des classes de l'application elle-même. Cela signifie que les classes d'extensions peuvent utiliser les classes de l'application (classes de base, interfaces d'API, ...). En utilisant un chargeur par fichier *.jar, chaque extension ne peut donc pas utiliser les classes d'une autre extension. En utilisant un seul chargeur pour tous les fichiers *.jar, les extensions peuvent utiliser des classes d'autres extensions. Cette dernière solution permet de créer des extensions utiles à d'autres extensions.
Toutefois l'inconvénient est que toutes les extensions sont chargées en mémoire même si elles ne sont pas utilisées. De plus, le nom complet des classes doit être unique.

### Chargement des classes d'extension

Le chargement des classes d'extension doit utiliser le chargeur de classes des fichiers *.jar trouvés dans le répertoire ext. Chaque extension doit utiliser un nom de paquetage unique afin que deux classes n'aient pas le même nom absolu.
Il faut aussi connaître la liste de noms des classes à charger. Cette liste peut être fixe en calculant un nom de paquetage unique à partir du nom du fichier archive JAR (donc une seule classe chargée), ou elle peut être lue à partir d'un fichier inclut dans le fichier JAR (par exemple, un fichier nommé extension.ini à la racine du fichier JAR).