Annotations

Le type Annotation est un des 5 types référence de java ; introduites en java 5.
N'affectent pas l'exécution du programme (en général), ajoutent de l'information au code ("meta data"). Servent au compilateur ou à différents outils (IDEs, déploiement).
Certaines annotations peuvent être examinées à l'exécution.
Java fournit 5 types prédéfinis (built-in) d'annotations.

Elles sont un type spécial d'interface, qui sont toutes (implicitement) des sous-interfaces de java.lang.annotation.Annotation.

Syntaxe à l'utilisation

Forme simple :
// fait partie des annotations prédéfinies
@Override
void mySuperMethod() { ... }
// une annotation custom
@Entity
Le nom de l'annotation est la chaîne de caractère qui suit le @.

Certaines peuvent contenir des éléments, nommés ou pas :
@Author(
   name = "Benjamin Franklin",
   date = "3/27/2003"
)
class MyClass() { ... }
Si une annotation ne possède qu'un seul élément nommé value, il peut être omis :
@SuppressWarnings(value = "unchecked")
void myMethod() { ... }

// équivalent à :

@SuppressWarnings("unchecked")
void myMethod() { ... }
Une déclaration peut utiliser plusieurs annotations :
@Author(name = "Jane Doe")
@EBook
class MyClass { ... }
Plusieurs annotations du même type peuvent être utilisées dans la même déclaration :
@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }

Où les utiliser ?

Les annotations peuvent être appliquées aux déclarations de classe, champs et méthodes de classe et aux autres éléments d'un programme.
Depuis java8, les annotations peuvent être utilisées lorsqu'on utilise un type, par exemple :

Création d'un objet
new @Interned MyObject();
Type casting
myString = (@NonNull String) str;
Clause implements
class UnmodifiableList implements @Readonly List<@Readonly T> { ... }
Clause throws
void monitorTemperature() throws @Critical TemperatureException { ... }

Annotations prédéfinies

Les 3 annotations de base dans java.lang sont @Deprecated, @Override, and @SuppressWarnings.
@SafeVarargs a été introduit dans java7 et @FunctionalInterface dans java8.

@SuppressWarnings

Permet d'éviter des warnings à la compilation ; 2 types de warning : deprecation et unchecked.
@SuppressWarnings("deprecation")
void useDeprecatedMethod() {
    objectOne.deprecatedMethod();
}
Pour utiliser les 2, notation tableau :
@SuppressWarnings({"unchecked", "deprecation"})
Une annotation non définie, par ex @SuppressWarnings(“toto”), sera ignorée par le compilateur.

@Deprecated

(= déprécié, ne doit plus être utilisé).
Doit être utilisé en lien avec javadoc (remarquer la différence de casse).
/**
 * @deprecated
 * Justification de la dépréciation
 */
@Deprecated
static void deprecatedMethod() { }
}
Exercice Deprecated

@Override

Pour signaler qu'un élément doit en surcharger un autre. Utilisé par exemple par un IDE pour signaler une typo, une erreur de type.
Utile aussi pour être certain d'avoir bien la bonne signature dans notre code.
Note : @Override est à comprendre comme "overrides ou implements" (même sémantique que extends pour les types génériques).
@Override 
int overriddenMethod() { }

@SafeVarargs

Utilisé lorsqu'on définit une méthode ou un constructeur avec des arguments variables de type générique.
Permet de désactiver à la compilation des warnings relatifs à des vérifications de type sur les arguments variables de type générique.
Exercice SafeVarargs

@FunctionalInterface

Permet de signaler une interface dont l'implémentation peut être exprimée sous forme de lambda expression.

Custom annotations

Pour utiliser des annotations personnalisées, il faut les définir.
Les annotations sont un type spécial d'interface, qui sont toutes (implicitement) des sous-interfaces de java.lang.annotation.Annotation.
On utilise @interface pour définir une annotation, et on définit les éléments de l'annotation avec des méthodes, comme dans une interface normale.
@interface MyAnnotation {
    String   value();
    String   name();
    int      age();
    String[] newNames();

}
Par rapport à une interface normale : Par exemple, pour normaliser la documentation d'une classe, on peut définir :
@interface ClassHeader {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   String[] reviewers(); // tableau
}
Et à l'utilisation :
@ClassHeader (
   author = "John Doe",
   date = "3/17/2002",
   currentRevision = 6,
   lastModified = "4/12/2004",
   lastModifiedBy = "Jane Doe",
   reviewers = {"Alice", "Bob", "Cindy"} // notation tableau
)
public class MyClass { ... }
Exercice EnhancementRequest

Meta-annotations (ou annotations d'annotations)

Permettent d'annoter la définition des annotations, pour spécifier où le nouveau type d'annotation peut être utilisé et comment il sera utilisé à la compilation et à l'exécution.

Par exemple, si on veut que les annotations soient utilisées par javadoc, il faut annoter la déclaration de l'annotation ("meta-annotation") avec @Documented:
import java.lang.annotation.*; // pour utiliser @Documented
@Documented 
@interface ClassHeader { ... }
Les meta-annotations possibles sont :
    @Target
    @Retention
    @Documented
    @Inherited
    @Repeatable

@Target

Permet de spécifier où la nouvelle annotation peut être utilisée.
Peut prendre les valeurs de l'enum java.lang.annotation.ElementType :
    TYPE
    FIELD
    METHOD
    PARAMETER
    CONSTRUCTOR
    LOCAL_VARIABLE
    ANNOTATION_TYPE
    PACKAGE
    TYPE_PARAMETER
    TYPE_USE


Par exemple pour indiquer qu'une annotation ne peut s'appliquer qu'aux méthodes :
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
public @interface MyAnnotation {
    String   value();
}

@Retention

Permet d'indiquer au compilateur et à l'interpréteur à quel endroit les annotations doivent être disponibles.
Peuvent prendre les valeurs de l'enum java.lang.annotation.RetentionPolicy : Exemple :
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Nickname {
    String[] value() default {};
}

@Inherited

Indique si les sous-classes d'une classe annotée héritent de l'annotation
import java.lang.annotation.Inherited

@Inherited
public @interface MyAnnotation {

}

@MyAnnotation
public class MySuperClass { ... }

public class MySubClass extends MySuperClass { ... }
MySubClass hérite de l'annotation.

@Documented

Indique si l'annotation sera visible dans le javadoc.
Par exemple, le code source de java.lang.FunctionalInterface :
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

Autres annotations

Java fournit un nombre réduit d'annotations dans java.lang et java.lang.annotation, mais les annotations personnalisées permettent d'en ajouter selon les besoin, ce qui est fait dans certains packages de l'API (liste complète dans la doc de java.lang.annotation.Annotation).

Il est possible aussi d'inclure dans son code des annotations définies dans des libs ("pluggable annotation type system").
Par exemple, on peut vouloir sécuriser son code contre des NullPointerException, définir une annotation @NonNull, et obtenir un warning à la compilation sur une instruction du type
@NonNull String str;
Exercice Installer et faire marcher checkerframework.org
Vous pouvez vous aider du code de test qui se trouve dans l'exemple checkerframework.org.
Le fichier README contient l'instruction javac pour compiler.
Les jars nécessaires sont dans le répertoire bin/checkerframework.org/

Par curiosité, vous pouvez aller voir le code implémentant le null checker sur github.