Programmation Java/Annotations

Un nouveau principe introduit par Java 5 est la gestion des méta-données grâce à un mécanisme appelé annotations.

Ces annotations sont ajoutées dans le code devant les classes et leurs membres (méthodes et champs) pour :

  • Ajouter des informations de documentation supplémentaires aux commentaires Javadoc[1],
  • Ajouter des contraintes de compilation (voir @Override par exemple),
  • Associer des méta-données qui peuvent être retrouvées par réflexion.

Malgré la similarité de syntaxe (arobase + nom) et de nom (@deprecated par exemple), il ne faut pas les confondre avec les tags Javadoc qui ne sont utilisés que par l'outil javadoc du JDK. De plus, une annotation correspond à un type de données (comme les classes, interfaces et énumérations). Ceci explique que la documentation d'un package possède une section supplémentaire nommée « Annotation Types » pour les annotations définies dans le package.

Toute entité (classe, interface, méthode ou champ) peut être précédée d'une ou plusieurs annotations contenant des méta-données renseignant le compilateur ou l'application elle-même. Ces annotations sont accessibles à l'exécution, en utilisant la réflexion, à partir de nouvelles méthodes de la classe java.lang.Class.

Syntaxe

modifier

La syntaxe d'une annotation est la suivante :

@annotation-type[ (name=value) * ]

La liste des paramètres entre parenthèses est optionnelle (vide par défaut). Elle contient une série de valeurs associées à un nom de champ définit par l'annotation.

Exemples

modifier

Exemple 1 :

@Author(
    name = "Moi",
    date = "02/01/2009"
)
class MyClass() { }

Exemple 2 :

@Override
void uneMethode() { }

Annotations existantes

modifier

Beaucoup d'annotations complètent les balises spéciales pour la documentation Javadoc.

@Deprecated

modifier
java.lang.Deprecated

Cette annotation marque une entité obsolète. Son utilisation génère un avertissement à la compilation, contrairement au tag @deprecated des commentaires Javadoc.

@Override

modifier
java.lang.Override

Cette annotation marque une méthode redéfinie.

Il ne s'agit pas d'une annotation de documentation mais d'un ajout de contrainte vérifiée à la compilation : une méthode marquée avec cette annotation doit obligatoirement être une méthode redéfinie de la classe mère. Dans le cas contraire (méthode non définie dans la classe mère), le compilateur génère une erreur annotation type not applicable to this kind of declaration.

À partir de Java 6, cette annotation peut aussi être utilisée pour les méthodes implémentant une interface.

@SuppressWarnings

modifier
java.lang.SuppressWarnings

Cette annotation signale au compilateur de supprimer certains avertissements à la compilation de l'entité.

Exemple 1 : pour supprimer les avertissements d'utilisations de méthodes obsolètes :

@SuppressWarnings("deprecation")
void uneMethode() { methodeObsolete(); }

Exemple 2 : pour supprimer les avertissements d'utilisations de méthodes obsolètes et d'utilisation de méthodes sans vérification de types (une version de la méthode avec types générique est préférable afin que le type d'élément soit vérifié) :

@SuppressWarnings({"unchecked", "deprecation"})
void uneMethode()
{
    Vector v=new Vector();
    methodeObsolete();
}

Créer de nouvelles annotations

modifier

La création est similaire à celle d'une interface.

Syntaxe

modifier
@interface identifiant
{
   type champ() [ default valeur ];
}

Exemple de définition:

@interface InfoClasse
{
    String auteur();
    int revision() default 1;
    String[] references();
}

Exemple d'utilisation:

@InfoClasse( auteur="Moi", references={"Reference1","Reference2"} )
class UneClasse { }

Cette nouvelle annotation peut elle-même être taguée avec des annotations du package java.lang.annotation indiquant l'utilisation qui en est faite.

Documentation pour Javadoc

modifier

Si l'annotation définit des informations à afficher dans la documentation générée par Javadoc, la définition de l'annotation doit utiliser l'annotation @Documented (java.lang.annotation.Documented).

Correction de l'exemple précédent :

@Documented
@interface InfoClasse
{
    String auteur();
    int revision() default 1;
    String[] references();
}

Informations disponibles à l'exécution

modifier

Pour que les informations d'une annotation soient disponibles à l'exécution, il faut annoter sa définition avec @Retention(RetentionPolicy.RUNTIME).

Exemple :

import java.lang.annotation.*; 

@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationPourExecution
{
   // Éléments d'information disponibles à l'exécution
}

Restreindre l'utilisation d'une annotation

modifier

La définition d'une annotation peut être annotée avec @Target pour spécifier avec quels types d'éléments l'annotation peut être utilisée.

La classe java.lang.annotation.ElementType définit les différents types qu'il est possible d'annoter.

Exemple :

import java.lang.annotation.*; 

@Target(ElementType.ANNOTATION_TYPE)
@interface PourAutreAnnotation
{
}

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@interface InfoMembreOuType
{
// Type = class/interface/enum
}

Accès aux annotations d'une classe

modifier

L'API de réflexion de Java permet d'accéder aux annotations d'une classe, méthode ou champ.

Exemple :

import java.lang.annotation.*; 

// ...

Class clas = objet.getClass(); // ou à partir d'une classe connue :   UneClasseConnue.class
Annotation[] annotations = clas.getAnnotations();

for (Annotation annotation : annotations)
{
    if (annotation instanceof InfoClasse)
    {
        InfoClasse infoClasse = (InfoClasse) annotation;
        System.out.println("auteur : " + infoClasse.auteur());
        System.out.println("revision : " + infoClasse.revision());
    }
}

Références

modifier