En 2005, avec l'arrivée de Java 5, nous avons découvert de nouvelles entités appelées annotations.

Les annotations sont une forme spéciale de métadonnées syntaxiques qui peuvent être déclarées dans le code. Ils sont utilisés pour analyser le code à la compilation ou à l'exécution. Vous pouvez considérer une annotation comme une étiquette, une balise ou un indice de compilation.

Vous avez probablement déjà rencontré des annotations. Par exemple, lors de la redéfinition d'une méthode de la classe parente, nous écrivons @Override avant la méthode elle-même. Cette annotation indique que la méthode du parent sera remplacée dans la classe enfant.

Syntaxe:


@Override
public int hashCode() {
      return super.hashCode();
}

Je tiens à noter tout de suite que les annotations ne s'appliquent pas uniquement aux méthodes. Ils sont utilisés avec des packages, des classes, des méthodes, des champs et des paramètres.

Pour comprendre le fonctionnement des annotations, familiarisons-nous d'abord avec le concept d'interface de marqueur. Depuis l'avènement de Java, les développeurs ont toujours eu besoin d'un moyen de marquer les classes afin d'effectuer certaines actions sur elles.

Avant Java 5, ils utilisaient une interface qui ne faisait pas ce que nous attendions des interfaces. Il n'avait pas de méthodes et pas de contrat. Cela a juste marqué une classe comme spéciale d'une certaine manière.

Une telle interface était appelée une interface de marqueur. D'après le nom, vous pouvez deviner que son but est de marquer des classes pour la JVM, le compilateur ou une bibliothèque. Certaines interfaces de marqueur, telles que Serializable , restent. Cette interface de marqueur nous permet d'indiquer que les instances d'une classe peuvent être sérialisées.

Comme nous l'avons vu, les interfaces de marqueur continuent de vivre même après l'introduction des annotations.

Annotations contre interfaces de marqueur :


@MyAnnotation
public class MyClass {}

public class MyClass implements MarkerInterface {}

Les deux approches ont le même objectif, mais il existe une nette différence dans leur mise en œuvre. Par exemple, considérons une interface et une annotation qui indiquent qu'une classe appartient à un type particulier.

Si nous utilisons une interface, nous marquons la classe. Si nous l'utilisons de manière incorrecte et qu'une erreur se produit, nous découvrirons le problème à la compilation et le programme ne fonctionnera pas.

Avec les annotations, tout n'est pas si simple : ici l'erreur sera détectée à l'exécution, ce qui signifie que le programme va démarrer, mais, sans surprise, il ne se terminera pas.

Notez que si nous devons marquer une classe pour une utilisation future, ses instances doivent être passées à une méthode spécifique :


public class MyInteger implements Sum {}
interface Sum {};

public static void main(String[] args) throws IOException {
        increase(new MyInteger());
}
 
public static void increase(Sum count) {
        // TODO
}

Une interface de marqueur fonctionne mieux ici.

Il est préférable d'utiliser des annotations lorsque nous avons besoin de quelque chose de plus, comme les paramètres pris en charge par les annotations.

Regardons les annotations standard dans le JDK :

Annotation Description Exemple
@Passer outre Spécifie qu'une méthode remplace la méthode d'une superclasse ou implémente une méthode d'une classe abstraite ou d'une interface.

@Override
public int hashCode() {
        return super.hashCode();
}
@Déprécié Marque le code comme obsolète.

@Deprecated
public abstract void method();
@Supprimer les avertissements Désactive les avertissements du compilateur pour l'élément annoté. Notez que si vous devez désactiver plusieurs catégories d'avertissements, elles doivent être placées entre accolades, par exemple @SuppressWarnings({"unchecked", "cast"}) .

public class DocumentsFolder {
   private List documents;

   @SuppressWarnings("unchecked")
public void addDocument(String document) {
            documents.add(document);
   }
}

Dans cet exemple, nous essayons d'ajouter à une liste qui n'a pas de type défini (un type générique). Le compilateur nous en avertira. C'est très utile, mais parfois il y a trop d'"avertissements" et ils peuvent être bruyants. Vous pouvez utiliser cette annotation de méthode et spécifier un type d'avertissement du compilateur comme argument. Il y a beaucoup de marqueurs, alors ne vous souciez pas de les mémoriser tous - IDEA vous dira généralement lequel ajouter.

Un autre exemple avec plusieurs arguments :


@SuppressWarnings({"unchecked", "deprecated"})