Annotations intégrées de Java
Les annotations intégrées Java les plus courantes — @Override, @Deprecated, @SuppressWarnings, @SafeVarargs, @FunctionalInterface.
La bibliothèque standard embarque un petit ensemble d'annotations dans java.lang que le compilateur traite de façon particulière. Aucune d'elles n'ajoute de comportement à votre code à l'exécution ; ce sont toutes des indications destinées à javac (ou, pour @Deprecated, à l'outillage en général). Savoir ce que chacune garantit — et ce qu'elle ne garantit pas — est l'essentiel du travail quotidien avec les annotations.
Les cinq que vous rencontrerez le plus souvent :
@Override— « cette méthode remplace une méthode d'un supertype. »@Deprecated— « n'utilisez pas ceci ; ça va disparaître. »@SuppressWarnings— « silence sur ces avertissements spécifiques dans cette portée. »@SafeVarargs— « ma méthode varargs ne pollue pas le tas. »@FunctionalInterface— « cette interface possède exactement une méthode abstraite. »
@Override
Placer @Override sur une méthode indique au compilateur que la méthode est censée remplacer une méthode de la superclasse ou implémenter une méthode d'interface. Si elle ne remplace rien en réalité (parce que le nom est mal orthographié, la signature est incorrecte, ou le parent a supprimé la méthode), javac fait échouer la compilation.
class Animal {
public String speak() { return "?"; }
}
class Dog extends Animal {
@Override
public String speak() { return "woof"; } // OK
@Override
public String spaek() { return "oops"; } // compile error: nothing to override
}Cela permet de détecter le type de bogue très difficile à trouver à l'exécution : une méthode de remplacement dont la signature s'éloigne de celle de la superclasse. Écrivez toujours @Override sur les méthodes que vous avez l'intention de remplacer. Elle a une rétention SOURCE, donc elle disparaît dès que la compilation est terminée.
@Deprecated
@Deprecated marque quelque chose — classe, méthode, champ, constructeur — comme déconseillé. Le compilateur avertit à chaque site d'utilisation. Depuis Java 9, l'annotation accepte deux éléments :
@Deprecated(since = "1.4", forRemoval = true)
public void oldApi() { ... }sincedocumente quand la dépréciation a commencé.forRemoval = trueest un signal plus fort : une future version prévoit de supprimer l'API. Le compilateur émet un avertissement de suppression (généralement plus fort qu'un simple avertissement de dépréciation) et l'outil Javadoc le signale différemment.
Contrairement à @Override, @Deprecated a une rétention RUNTIME — les outils bytecode, les IDE et la réflexion peuvent tous la voir. La balise Javadoc associée @deprecated (en minuscules, dans un commentaire de documentation) porte l'explication ; l'annotation déclenche l'outillage.
@SuppressWarnings
Quand le compilateur a presque toujours raison mais a tort ici, @SuppressWarnings fait taire une catégorie d'avertissements à l'intérieur de l'élément annoté :
@SuppressWarnings("unchecked")
List<String> strings = (List<String>) raw; // raw cast intentional
@SuppressWarnings({"unchecked", "rawtypes"})
public void uglyButNeeded() { ... }L'élément string désigne une catégorie d'avertissements ; les plus courantes sont unchecked, rawtypes, deprecation, serial, unused, removal. Les compilateurs peuvent en accepter d'autres. Deux règles pour garder cela propre :
- Annotez la portée la plus petite possible. Supprimez les avertissements sur une variable locale ou une seule méthode, jamais sur une classe, sauf si chaque ligne en a réellement besoin.
- Accompagnez d'un commentaire expliquant pourquoi. Un
@SuppressWarnings("unchecked")nu à côté d'un cast laisse le prochain lecteur se demander si le cast est vraiment sûr.
@SafeVarargs
Une méthode varargs dont le type de paramètre contient un paramètre de type présente un problème subtil : au site d'appel, le compilateur peut avoir à créer un tableau d'un type générique, ce qui n'est pas sûr. Le compilateur avertit à ce sujet avec le message « possible heap pollution ». Si l'auteur a vérifié que le corps ne fuit pas le tableau et n'y écrit pas de types incorrects, @SafeVarargs fait taire l'avertissement :
@SafeVarargs
public final <T> List<T> listOf(T... items) {
return java.util.List.of(items); // only reads the items, never stores other types
}Règles :
- Uniquement légal sur les méthodes qui ne peuvent pas être redéfinies —
static,final, ouprivate, ainsi que les constructeurs de type record. - L'annotation est une promesse. Si votre corps écrit réellement dans le tableau varargs avec un type incorrect, le cast au site d'appel peut échouer ultérieurement avec une
ClassCastExceptiondéroutante.
Elle a une rétention SOURCE.
@FunctionalInterface
Une interface fonctionnelle est une interface avec exactement une méthode abstraite — la forme que partagent Runnable, Callable, Comparator et Function. Les expressions lambda et les références de méthodes ciblent les interfaces fonctionnelles. L'annotation rend l'intention explicite et demande au compilateur d'appliquer la règle de la méthode abstraite unique :
@FunctionalInterface
public interface StringMapper {
String map(String input); // the single abstract method
default StringMapper andThen(StringMapper next) { // default methods are allowed
return s -> next.map(map(s));
}
}Si vous ajoutez ultérieurement une deuxième méthode abstraite, la compilation échoue immédiatement. Sans l'annotation, l'interface cesserait silencieusement d'être utilisable comme cible lambda — ce qu'un utilisateur ne remarquerait qu'au site d'appel.
Comme @Override, elle a une rétention SOURCE.
Un exemple complet : observer l'application par le compilateur
Ce programme illustre les quatre annotations appliquées par le compilateur et montre ce que l'environnement d'exécution peut voir ensuite. Les points intéressants : la méthode @SafeVarargs se compile sans l'avertissement qui apparaît sans annotation ; le @FunctionalInterface est réfléchi via les règles de comptage SAM ; les valeurs d'éléments de @Deprecated sont accessibles à l'exécution.
Ce que l'on retient de l'exécution :
@FunctionalInterfacea accompli son travail à la compilation en garantissant queStringMapperest un type SAM :String::toUpperCaseet le lambdas -> s + \"!\"s'y sont tous deux liés proprement. Si quelqu'un ajoutait une deuxième méthode abstraite à l'interface, la compilation échouerait et ces expressions cesseraient de se résoudre.- La ligne
@Overrideétait la bookkeeping qui garantissait queChild.describe()remplace bienParent.describe(). L'appel polymorphe atterrissant sur\"child\"le confirme ; si la signature avait dérivé (nom différent, type de retour différent), la compilation aurait échoué au lieu de produire un comportement incorrect à l'exécution. @Deprecatedest la seule annotation ici qui survit à la compilation. La réflexion a réussi à extrairesince=1.4etforRemoval=truedu fichier de classe. La méthode elle-même a quand même fonctionné —@Deprecatedavertit, elle ne désactive pas.@SafeVarargsa supprimé l'avertissement de compilation « possible heap pollution » tout en conservant l'appel de type sûr. Notez que la méthode eststatic, ce qui satisfait la règle « ne peut pas être redéfinie ». Supprimer l'annotation compilerait mais déclencherait des avertissements lors dejavac; l'ajouter sur une méthode non-static, non-final, non-private serait une erreur de compilation.@SuppressWarningsn'a laissé aucune trace à l'exécution — le tableau d'annotations affiché pourparseOrZeroest vide. C'est tout l'objet de la rétentionSOURCE: l'annotation fait son travail pendant la compilation puis disparaît, gardant le fichier de classe sans encombrement.
Autres annotations intégrées à connaître
Un petit nombre d'annotations moins courantes de la bibliothèque standard, brièvement :
@SuppressWarnings("preview")— pour le code utilisant des fonctionnalités de langage en préversion (Java 14+).@Native(java.lang.annotation.Native) — marque une constante pouvant être référencée depuis du code natif ; utilisée par les outils qui génèrent des en-têtes JNI.@Generated(javax.annotation.processing.Generated, Java 9+) — ajoutée par les générateurs de code sur les fichiers qu'ils émettent.@Documented,@Retention,@Target,@Inherited,@Repeatable— ce sont des méta-annotations ; couvertes dans le chapitre suivant.
Vous écrirez rarement les trois premières à la main. Les méta-annotations sont la passerelle vers l'écriture de vos propres types d'annotations, et la réflexion est la façon dont les annotations à rétention runtime comme @Deprecated sont lues en retour — comme le montre l'exemple ci-dessus.