2024-10-04
Source | |
Branch |
|
Java |
|
Docker |
|
Les annotations en Java sont des métadonnées que l’on associe à différentes entités du code (classes, méthodes, attributs, etc.) afin de fournir des informations supplémentaires à propos de celui-ci.
À quoi servent les annotations ?
Syntaxe de base
@
suivi de son nom. Par exemple :Il existe plusieurs annotations standards couramment utilisées en Java :
@Deprecated
( Lien vers la documentation d’Oracle pour @Deprecated ) : Signale qu’une classe, méthode, etc. est déconseillée et qu’il est préférable d’utiliser une alternative.@FunctionalInterface
( Lien vers la documentation d’Oracle pour @FunctionalInterface ) : Indique qu’une interface peut être utilisée pour la programmation fonctionnelle, ne pouvant avoir qu’une seule méthode abstraite.@Override
( Lien vers la documentation d’Oracle pour @Override ) : Spécifie qu’une méthode redéfinit une méthode héritée d’une classe parente.@SuppressWarnings
( Lien vers la documentation d’Oracle pour @SuppressWarnings ) : Demande au compilateur d’ignorer certains types d’avertissements pour un élément de code spécifique.@SafeVarargs
( Lien vers la documentation d’Oracle pour @SafeVarargs ) : Indique que la méthode utilisant des arguments de type générique ne provoque pas de problèmes de sécurité de type au moment de l’exécution.Une annotation se déclare dans un fichier qui a le même nom comme une classe ou une interface.
Elle peut alors être utilisée partout sur une classe.
Les paramètres d’une annotation, appelés éléments, déterminent les informations supplémentaires que l’on peut associer à un élément de code. Ces éléments peuvent être de différents types :
Définition d’une annotation
public @interface MyAnnotation {
String message(); // Message associé à l'annotation
int size() default 10; // Taille maximale (valeur par défaut : 10)
Digit digit() default Digit.UN; // Choix entre UN et DEUX (par défaut : UN)
Class<?>[] classes() default {}; // Tableau de classes associées
enum Digit {UN, DEUX} // Valeurs possibles pour le chiffre
}
et utilisation sur une classe ou une méthode
Si leur seul attribut obligatoire s’appelle value
, il est possible d’omettre son nom lors de l’annotation.
L’annotation @Target
définit la portée d’une annotation en spécifiant les éléments de programme (classes, méthodes, champs, paramètres, etc.) auxquels elle peut être appliquée. Les valeurs possibles pour @Target
sont : ANNOTATION_TYPE
, CONSTRUCTOR
, FIELD
, LOCAL_VARIABLE
, METHOD
, PACKAGE
, MODULE
, PARAMETER
, TYPE
, TYPE_PARAMETER
.
Par exemple, en utilisant @Target(ElementType.METHOD)
, on indique que l’annotation ne peut être appliquée qu’à des méthodes.
Par défaut, une annotation ne peut être appliquée qu’une seule fois à un élément.
ou les autoriser à être répétables grâce à l’annotation @Repeatable
, évitant ainsi la déclaration explicite d’un tableau d’annotations.
Pour annoter un package, utilisez le fichier spécial package-info.java
.
Exemple:
java filename="package-info.java" @SuppressWarnings("unused") package com.example;
@Retention
Détermine à quel moment du cycle de vie d’une application une annotation est accessible.Valeur | Description |
---|---|
SOURCE | Utilisée uniquement par le compilateur. |
CLASS | Conservée dans le fichier .class. |
RUNTIME | Conservée à l’exécution. |
@Inherited
Indique si une annotation déclarée sur une classe est également présente sur ses sous-classes.
@Documented
Indique qu’une annotation doit être incluse dans la documentation générée par le javadoc.
public class MyClass {
@MyAnnotation
public void m1() {System.out.println("Hello1");}
@MyAnnotation(status=MyAnnotation.Status.IN_PROGRESS)
public void m2() {System.out.println("Hello2");}
@MyAnnotation(status=MyAnnotation.Status.DONE)
public void m3() {System.out.println("Hello3");}
public void m4() {System.out.println("Hello4");}
}
Les annotations peuvent être utilisées à l’exécution grâce à l’introspection, à condition d’avoir la rétention RUNTIME
.
getMethod()
, getField()
, getConstructor()
, etc. permettent d’accéder aux membres d’une classe.L’exemple ci-dessous affiche les méthodes de la classe précédente annotées avec @MyAnnotation
.
// Récupère une instance de la classe MyClass
Class aClass = new MyClass().getClass();
// Récupère toutes les méthodes déclarées dans MyClass et les filtre pour ne garder que celles annotées avec @MyAnnotation
Arrays.stream(aClass.getDeclaredMethods())
.filter(m->m.getAnnotation(MyAnnotation.class)!=null)
.map(m->"La méthode %s a comme statut %s.".formatted(m.getName(),m.getAnnotation(MyAnnotation.class).status()))
.forEach(System.out::println);
La méthode m2 a comme statut IN_PROGRESS.
La méthode m1 a comme statut DONE.
La méthode m3 a comme statut DONE.
Il est aussi possible d’invoquer dynamiquement ces méthodes.
// Invokes methods annotated with @MyAnnotation (status=DONE) on the provided object
MyClass myObject = new MyClass();
Arrays.stream(myObject.getClass().getDeclaredMethods())
.filter(m -> m.getAnnotation(MyAnnotation.class) != null &&
m.getAnnotation(MyAnnotation.class).status().equals(MyAnnotation.Status.DONE))
.forEach(m -> {
try {
m.invoke(myObject);
} catch (Exception e) {
System.err.println("Error invoking method " + m.getName() + ": " + e.getMessage());
}
});
Hello1
Hello3
E. Bruno