Présentation des annotations intégrées à Java

Présentation des annotations intégrées à Java

1. Overview

Dans cet article, nous allons parler d'une fonctionnalité principale du langage Java - les annotations par défaut disponibles dans le JDK.

2. Qu'est-ce qu'une annotation

En termes simples, les annotations sontJava types that are preceded by an “@” symbol.

Java a des annotations depuis la version 1.5. Depuis, ils ont façonné la façon dont nous concevons nos applications.

Spring et Hibernate sont d'excellents exemples de frameworks qui s'appuient fortement sur les annotations pour permettre diverses techniques de conception.

Fondamentalement,an annotation assigns extra metadata to the source code it’s bound to. En ajoutant une annotation à une méthode, une interface, une classe ou un champ, nous pouvons:

  1. Informer le compilateur des avertissements et des erreurs

  2. Manipuler le code source lors de la compilation

  3. Modifier ou examiner le comportement à l'exécution

3. Annotations intégrées Java

Maintenant que nous avons passé en revue les principes de base, examinons quelques annotations fournies avec le noyau Java. Premièrement, il y en a plusieurs qui informent la compilation:

  1. @Passer outre

  2. @Supprimer les avertissements

  3. @Deprecated

  4. @SafeVarargs

  5. @Interface fonctionnelle

Ces annotations génèrent ou suppriment les avertissements et les erreurs du compilateur. Leur application cohérente est souvent une bonne pratique car leur ajout peut prévenir les erreurs futures du programmeur.

L'annotation@Override est utilisée pour indiquer qu'une méthode remplace ou remplace le comportement d'une méthode héritée.

@SuppressWarnings indique que nous voulons ignorer certains avertissements d'une partie du code. L'annotation@SafeVarargs agit également sur un type d'avertissement lié à l'utilisation de varargs.

L'annotation@Deprecated peut être utilisée pour marquer une API comme n'étant plus destinée à être utilisée.

Pour tous ceux-ci, vous pouvez trouver des informations plus détaillées dans les articles liés.

3.1. @FunctionalInterface

Java 8 nous permet d’écrire du code de manière plus fonctionnelle.

Single Abstract Method interfaces en sont une grande partie. If we intend a SAM interface to be used by lambdas, we can optionally mark it as such with @FunctionalInterface:

@FunctionalInterface
public interface Adder {
    int add(int a, int b);
}

Comme@Override avec les méthodes,@FunctionalInterface déclare nos intentions avecAdder.

Maintenant, que nous utilisions@FunctionalInterface ou non, nous pouvons toujours utiliserAdder de la même manière:

Adder adder = (a,b) -> a + b;
int result = adder.add(4,5);

Mais, si nous ajoutons une deuxième méthode àAdder,, le compilateur se plaindra:

@FunctionalInterface
public interface Adder {
    // compiler complains that the interface is not a SAM

    int add(int a, int b);
    int div(int a, int b);
}

Maintenant, cela aurait été compilé sans l'annotation@FunctionalInterface. Alors, qu'est-ce que cela nous donne?

Comme@Override, cette annotation nous protège contre les futures erreurs du programmeur. Even though it’s legal to have more than one method on an interface, it isn’t when that interface is being used as a lambda target. Sans cette annotation, le compilateur se briserait dans les dizaines d'endroits oùAdder était utilisé comme lambda. Maintenant,it just breaks in Adder itself.

4. Méta-annotations

Ensuite, les méta-annotations sont des annotations qui peuvent être appliquées à d'autres annotations.

Par exemple, ces méta-annotations sont utilisées pour la configuration des annotations:

  1. @Cible

  2. @Rétention

  3. @Hérité

  4. @Documenté

  5. @Répétable

4.1. @Target

La portée des annotations peut varier en fonction des besoins. Bien qu'une annotation ne soit utilisée qu'avec des méthodes, une autre annotation peut être utilisée avec des déclarations de constructeur et de champ.

Pour déterminer les éléments cibles d'une annotation personnalisée, nous devons l'étiqueter avec une annotation@Target.

@Target peut fonctionner aveceight different element types. Si nous regardons le code source de @SafeVarargs, alors nous pouvons voir qu'il ne doit être attaché qu'à des constructeurs ou des méthodes:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {
}

4.2. @Retention

Certaines annotations sont destinées à servir d'indices pour le compilateur, tandis que d'autres sont utilisées au moment de l'exécution.

We use the @Retention annotation to say where in our program’s lifecycle our annotation applies.

Pour ce faire, nous devons configurer@Retention avec l'une des trois stratégies de rétention:

  1. RetentionPolicy.SOURCE – visible ni par le compilateur ni par le runtime

  2. RetentionPolicy.CLASS - visible par le compilateur

  3. RetentionPolicy.RUNTIME – visible par le compilateur et le runtime

@Retention est par défautRetentionPolicy.SOURCE.

Si nous avons une annotation qui devrait être accessible au moment de l'exécution:

@Retention(RetentionPolicy.RUNTIME)
@Target(TYPE)
public @interface RetentionAnnotation {
}

Ensuite, si nous ajoutons des annotations à une classe:

@RetentionAnnotation
@Deprecated
public class AnnotatedClass {
}

Nous pouvons maintenant réfléchir surAnnotatedClass pour voir combien d'annotations sont conservées:

@Test
public void whenAnnotationRetentionPolicyRuntime_shouldAccess() {
    AnnotatedClass anAnnotatedClass = new AnnotatedClass();
    Annotation[] annotations = anAnnotatedClass.getClass().getAnnotations();
    assertThat(annotations.length, is(1));
}

La valeur est 1 car @RetentionAnnotation a une politique de rétention deRUNTIME alors que@Deprecated n'en a pas.

4.3. @Inherited

Dans certaines situations, une sous-classe peut être nécessaire pour que les annotations soient liées à une classe parente.

Nous pouvons utiliser l'annotation@Inherited pour que notre annotation se propage d'une classe annotée vers ses sous-classes.

Si nous appliquons@Inherited à notre annotation personnalisée puis l'appliquons àBaseClass:

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedAnnotation {
}

@InheritedAnnotation
public class BaseClass {
}

public class DerivedClass extends BaseClass {
}

Ensuite, après avoir étendu la BaseClass, nous devrions voir queDerivedClass semble avoir la même annotation à l'exécution:

@Test
public void whenAnnotationInherited_thenShouldExist() {
    DerivedClass derivedClass = new DerivedClass();
    InheritedAnnotation annotation = derivedClass.getClass()
      .getAnnotation(InheritedAnnotation.class);

    assertThat(annotation, instanceOf(InheritedAnnotation.class));
}

Sans l'annotation@Inherited, le test ci-dessus échouerait.

4.4. @Documented

Par défaut, Java ne documente pas l'utilisation d'une annotation dans Javadocs.

But, we can use the @Documented annotation to change Java’s default behavior.

Si nous créons une annotation personnalisée qui utilise@Documented:

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelCell {
    int value();
}

Et appliquez-le à l'élément Java approprié:

public class Employee {
    @ExcelCell(0)
    public String name;
}

Ensuite, le Javadoc deEmployee révélera l'utilisation de l'annotation:

image

4.5. @Repeatable

Parfois, il peut être utile de spécifier plusieurs fois la même annotation sur un élément Java donné.

Avant Java 7, nous devions regrouper les annotations dans une seule annotation de conteneur:

@Schedules({
    @Schedule(time = "15:05"),
    @Schedule(time = "23:00")
})
void scheduledAlarm() {
}

Cependant, Java 7 a apporté une approche plus propre. Withthe @Repeatable annotation,we can make an annotation repeatable:

@Repeatable(Schedules.class)
public @interface Schedule {
    String time() default "09:00";
}

Pour utiliser@Repeatable, nous devons également avoir une annotation de conteneur. Dans ce cas, nous réutiliserons@Schedules:

public @interface Schedules {
    Schedule[] value();
}

Bien entendu, cela ressemble beaucoup à ce que nous avions avant Java 7. Mais, la valeur maintenant est que le wrapper@Schedules n'est plus spécifié lorsque nous devons répéter@Schedule:

@Schedule
@Schedule(time = "15:05")
@Schedule(time = "23:00")
void scheduledAlarm() {
}

Comme Java requiert les annotations du wrapper, il nous a été facile de passer des listes d’annotations antérieures à Java 7 à des annotations répétables.

5. Conclusion

Dans cet article, nous avons parlé des annotations intégrées Java que tout développeur Java devrait connaître.

Comme toujours, tous les exemples de l'article se trouventover on GitHub.