Annotations Java
Présentation des annotations
Les annotations (Annotation) sont une forme de métadonnées introduite dans Java 5. Elles offrent un moyen d'ajouter des métadonnées au code, lesquelles peuvent être lues et traitées par le compilateur, les outils de développement ou l'environnement d'exécution.
Caractéristiques principales
- Syntaxe spéciale commençant par
@ - N'affectent pas la logique du programme mais peuvent influencer le comprotement du compilateur ou de l'exécution
- Peuvent être attachées à des packages, classes, méthodes, champs, paramètres, etc.
- Traitées à la compilation ou à l'exécution
Annotations intégrées (built-in)
Les annotations intégrées sont celles déjà définies dans le langage Java, utilisables directement. Elles font partie de la bibliothèque standard.
-
@Override– Indique qu'une méthode redéfinit une méthode de la classe parente ou d'une interface. Si la méthode annotée ne redéfinit pas correctement, le compilateur génère une erreur.class Parent { void afficher() { System.out.println("Parent"); } } class Enfant extends Parent { @Override void afficher() { System.out.println("Enfant"); } } -
@Deprecated– Marque des éléments obsolètes. Le compilateur émet un avertissement lors de leur utilisation.@Deprecated void ancienneMethode() { System.out.println("Cette méthode est dépréciée."); } -
@SuppressWarnings– Supprime les avertissements du compilateur. Peut spécifier le type d'avertissement à ignorer, ex:@SuppressWarnings("unchecked").@SuppressWarnings("unchecked") void methodeNonSure() { java.util.List liste = new java.util.ArrayList(); liste.add("Bonjour"); } -
@FunctionalInterface– Marque une interface fonctionnelle (une seule méthode abstraite). Si l'interface ne respecte pas cette condition, le compilateur émet une erreur.@FunctionalInterface interface MaFonction { void executer(); }
Méta-annotations
Les méta-anontations sont des annotations spéciales utilisées pour définir les caractéristiques et le comportement d'autres annotations. Elles s'appliquent aux annotations elles-mêmes, et non aux classes ou méthodes ordinaires.
Liste des méta-annotations
-
@Retention– Définit la stratégie de rétention : à quel stade l'annotation est disponible. Les valeurs possibles :@Retention(RetentionPolicy.RUNTIME) @interface MonAnnotation { String valeur(); }SOURCE: uniquement dans le code source, ignorée à la compilation.CLASS: conservée dans le bytecode mais inaccessible à l'exécution.RUNTIME: conservée à l'exécution, accessible par réflexion.
-
@Target– Définit les éléments cibles sur lesquels l'annotation peut s'appliquer.@Target(ElementType.METHOD) @interface AnnotationMethode { String valeur(); }Valeurs possibles (liste non exhaustive) :
TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE. -
@Documented– Indique que l'annotation doit apparaître dans la documentation Java générée.@Documented @interface AnnotationDocumentee { String valeur(); } -
@Inherited– Permet à l'annotation d'être héritée par les sous-classes.@Inherited @interface AnnotationHeritee { String valeur(); } -
@Repeatable– Permet d'appliquer plusieurs fois la même annotation au même endroit (Java 8+).@Repeatable(MesAnnotations.class) public @interface MonAnnotation {}
Comparaison annotations intégrées vs méta-annotations
| Caractéristique | Annotation intégrée | Méta-annotation |
|---|---|---|
| Objectif | Fournir des fonctionnalités prédéfinies | Définir le comportement d'autres annotations |
| Utilisation | Directement sur le code | Pour créer des annotations personnalisées |
| Rétention | Souvent RUNTIME, mais variable | Souvent RUNTIME, mais adaptable |
| Cible | Classes, méthodes, champs, etc. | Uniquement les annotations |
| Exemples | @Override, @Deprecated |
@Retention, @Target |
Réflexion en Java
La réflexion permet d'inspecter et de manipuler les classes, méthodes, champs et annotations à l'exécution via l'objet Class.
Obtenir un objet Class
// Méthode 1 : nom complet de la classe (courant)
Class<?> clazz1 = Class.forName("com.exemple.Etudiant");
System.out.println(clazz1);
// Méthode 2 : via la classe elle-même (utile pour les paramètres)
Class<Etudiant> clazz2 = Etudiant.class;
System.out.println(clazz2);
// Méthode 3 : via une instance existante
Etudiant etu = new Etudiant();
Class<?> clazz3 = etu.getClass();
System.out.println(clazz3);
API principales de la classe Class
| Méthode | Rôle | Exemple |
|---|---|---|
forName() |
Charger dynamiquement une classe | Class.forName("java.lang.String") |
getConstructor() |
Obtenir un constructeur | clazz.getConstructor(String.class) |
getDeclaredField() |
Obtenir un champ | clazz.getDeclaredField("nom") |
getMethod() |
Obtenir une méthode | clazz.getMethod("longueur") |
getAnnotation() |
Obtenir une annotation | clazz.getAnnotation(Deprecated.class) |
Classe Constructor
// Exemple d'obtention et d'utilisation d'un constructeur
Constructor<String> constructeur = String.class.getConstructor(byte[].class, java.nio.charset.Charset.class);
String chaine = constructeur.newInstance(new byte[]{65,66,67}, java.nio.charset.StandardCharsets.UTF_8);
Classe Method
// Appel de méthode par réflexion
Method sousChaine = String.class.getMethod("substring", int.class, int.class);
String resultat = (String) sousChaine.invoke("Bonjour le monde", 0, 7);
Classe Field
// Modification de champ privé
Field champ = Utilisateur.class.getDeclaredField("age");
champ.setAccessible(true); // ignorer la visibilité privée
champ.set(utilisateur, 30);
int age = (int) champ.get(utilisateur);
Proxies dynamiques
Caractéristique : ajouter des fonctionnalités sans modification du code existant.
// Interface à proxifier
interface Service {
void servir();
}
// Gestionnaire d'invocation
class GestionnaireProxy implements InvocationHandler {
private Object cible;
public GestionnaireProxy(Object cible) {
this.cible = cible;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Avant l'appel");
Object resultat = method.invoke(cible, args);
System.out.println("Après l'appel");
return resultat;
}
}
// Création du proxy
Service serviceReel = new ServiceReel();
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
new GestionnaireProxy(serviceReel));
Manipulation de champs et cnostructeurs par réflexion




