The @Repeatable Meta-Annotation

There are situations where we would like to apply the same annotation multiple times on the same program element. This makes more sense if the annotation is not a marker annotation and we want to apply the annotation with different values.

The declaration of the Choice annotation type below declares two annotation type elements that specify the color and the size.

Click here to view code image

import java.lang.annotation.*;
enum Size {S, M, L, XL}
@Retention(RetentionPolicy.RUNTIME)
@interface Choice {
  String color();
  Size size() default Size.L;
}

We wish to apply the @Choice annotation multiple times to the class Item to indicate different combinations of color and size. However, the code below will not compile.

Click here to view code image

@Choice(color=”Green”, size=Size.S)                    // Compile-time error!
@Choice(color=”Yellow”, size=Size.XL)                  // Compile-time error!
@Choice(color=”Red”, size=Size.M)                      // Compile-time error!
@Choice(color=”White”)                                 // Compile-time error!
class Item {}

The compiler complains that the @Choice annotation is not repeatable. The declaration of the Choice annotation type must be declared with the meta-annotation @Repeatable, which is a single-element meta-annotation having the following declaration:

Click here to view code image

@Documented
@Retention(RUNTIME)
@Target(ANNOTATION_TYPE)
public @interface Repeatable {
  Class<? extends Annotation> value();  // Indicates the containing annot type
}

In order to apply the @Repeatable meta-annotation on an annotation type, the value of the containing annotation type must be supplied—that is, the Class object representing the containing annotation type in its single-element type declaration must be specified. Keep in mind that all annotation types implement the Annotation interface.

We can augment the declaration of the Choice annotation with the meta-annotation @Repeatable that specifies the containing annotation type Choices (which has not been declared as yet).

Click here to view code image

import java.lang.annotation.*;
enum Size {S, M, L, XL}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Choices.class)    // @Repeatable specifies the container
                              // annotation type Choices.
@interface Choice {
  String color();
  Size size() default Size.L;
}

The containing annotation type (Choices) of a repeatable annotation type (Choice) must declare a value() method whose return type is an array of the repeatable annotation type (Choice[]).

Click here to view code image

@Retention(RetentionPolicy.RUNTIME)
@interface Choices {
  Choice[] value();             // Return value is an array of Choice.
}

The upshot of the setup described above is that the multiple applications of a repeatable annotation (@Choice) will be stored in the array of the repeatable annotation type (Choice[]) declared in the containing annotation type (@Choices).

The multiple applications of the @Choice annotation on the class Item above will now compile. We can call the method AnnotationPrinter.printAllAnnotations() in Example 25.5, p. 1592, with the Class<Item> object Item.class to print the annotations on the class Item. (Output shown below has been edited to fit on the page.)

Click here to view code image

Annotations for ‘class Item’:
  @Choices(value={@Choice(size=S, color=”Green”),
                  @Choice(size=XL, color=”Yellow”),
                  @Choice(size=M, color=”Red”),
                  @Choice(size=L, color=”White”)})

A Containing Annotation Type (CAT) for a Repeatable Annotation Type (RAT) must satisfy the following conditions:

  • RAT must specify CAT.class as the value of the single element of its meta-annotation @Repeatable.
  • CAT must declare a value() annotation type element whose type is RAT[].
  • Any additional annotation type elements declared in CAT must have a default value.

Click here to view code image

@Retention(RetentionPolicy.RUNTIME)
@interface Choices {
  Choice[] value();                  // Return value is an array of Choice.
  double minPrice() default 1.00;    // Must specify a default value.
}

  • The retention policy for CAT is at least as long as the retention policy for RAT. For example, if RAT has retention policy RUNTIME, the retention policy for CAT cannot be CLASS or SOURCE. However, if RAT has retention policy CLASS, the retention policy for CAT can be CLASS or RUNTIME.
  • RAT is applicable to at least the same kind of program elements as CAT—that is, the list of targets to which RAT can be applied cannot be shorter than the list of targets to which CAT can be applied. For example, if CAT is applicable to the targets METHOD and FIELD, then RAT must be applicable to at least these targets.
  • If RAT has the meta-annotation @Documented, then so must CAT.
  • If RAT has the meta-annotation @Inherited, then so must CAT.

In addition, it is a compile-time error if the meta-annotation @Repeatable in the repeatable annotation type does not specify a containing annotation type for the repeatable annotation type.

Click here to view code image

@Repeatable(Choices.class)        // Compile-time error since invalid return type
                                  // at (1).
@interface Choice {
  String color();
  Size size() default Size.L;
}

@Retention(RetentionPolicy.RUNTIME)
@interface Choices {
  String[] value();                // (1) Invalid return type.
}

Leave a Reply

Your email address will not be published. Required fields are marked *