W3docs

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() { ... }
  • since documente quand la dépréciation a commencé.
  • forRemoval = true est 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 :

  1. 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.
  2. 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, ou private, 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 ClassCastException dé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.

java— editable, runs on the server

Ce que l'on retient de l'exécution :

  • @FunctionalInterface a accompli son travail à la compilation en garantissant que StringMapper est un type SAM : String::toUpperCase et le lambda s -> 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 que Child.describe() remplace bien Parent.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.
  • @Deprecated est la seule annotation ici qui survit à la compilation. La réflexion a réussi à extraire since=1.4 et forRemoval=true du fichier de classe. La méthode elle-même a quand même fonctionné — @Deprecated avertit, elle ne désactive pas.
  • @SafeVarargs a supprimé l'avertissement de compilation « possible heap pollution » tout en conservant l'appel de type sûr. Notez que la méthode est static, ce qui satisfait la règle « ne peut pas être redéfinie ». Supprimer l'annotation compilerait mais déclencherait des avertissements lors de javac ; l'ajouter sur une méthode non-static, non-final, non-private serait une erreur de compilation.
  • @SuppressWarnings n'a laissé aucune trace à l'exécution — le tableau d'annotations affiché pour parseOrZero est vide. C'est tout l'objet de la rétention SOURCE : 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.

Pratique

Pratique
Une méthode est déclarée `public <T> T[] toArray(T... values)` et le compilateur avertit d'une 'possible heap pollution from parameterized vararg type'. L'auteur examine le corps, confirme qu'il écrit uniquement des éléments de type T dans le tableau, et ajoute `@SafeVarargs`. Pourquoi le compilateur rejette-t-il l'annotation ?
Une méthode est déclarée `public <T> T[] toArray(T... values)` et le compilateur avertit d'une 'possible heap pollution from parameterized vararg type'. L'auteur examine le corps, confirme qu'il écrit uniquement des éléments de type T dans le tableau, et ajoute `@SafeVarargs`. Pourquoi le compilateur rejette-t-il l'annotation ?
Was this page helpful?