Pages - Menu

Monday, September 3, 2012

Java 8 Features: Discover Repeating Annotations

Java 8 will bring many features including lambdas, virtual extension methods, type annotations as well as various library enhancements. The full list is available on the OpenJDK/JDK8 website.

In this blog post, I would like to describe a less advertised feature: repeating annotations. The implementation in the Java 8 compiler was pushed a few days ago. More information and discussions can be found on the dedicated OpenJDK mailing list. It is important to stress that this feature was designed for EE library designers and users. The usability requirements are therefore different than standard Java users.

Currently Java forbids more than one annotation of a given annotation type to be specified on a declaration. For this reason, the following code is invalid:

@interface Foo { int value(); }

@Foo(1) @Foo(2) // error: Duplicate annotation
class Functr{}

Java EE programmers often make use of an idiom to circumvent this restriction: declare a new annotation which contains an array of the annotation you want to repeat. It looks like this:

@interface Foo { int value(); }
@interface Foos {
    Foo[] value();
}

@Foos({@Foo(1), @Foo(2)})
class Functr{}

Java 8 essentially removes this restriction. Programmers will now be able to specify multiple annotations of the same annotation type on a declaration provided they stipulate that the annotation is repeatable. It is not the default behaviour, annotations have to opt-in to be repeatable.

A set up is required to specify that an annotation can be repeated. You specify the containing annotation type using @ContainedBy and the repeatable annotation type using @ContainerFor:

@ContainedBy(Foos.class)
@interface Foo { int value(); }

@ContainerFor(Foo.class)
@interface Foos {
    Foo[] value();
}

@Foo(1) @Foo(2) // valid in Java 8!
class Functr{}

There is some discussion as to why this set up is actually required because it adds code verbosity. In a nutshell, it is needed for compatibility reasons with the updated reflection API. In fact, the previous idiom and the new set up compile down to the same structure (provided @Retention(RetentionPolicy.CLASS) is specified on the annotations otherwise annotations are not stored in the generated class file by the compiler). For this reason, the reflection API needs a mechanism to differentiate between implicit and explicit repetitions. The language designers introduced the two new markups to tackle this issue.

Now you might ask why do we actually need @ContainedBy and @ContainedFor? Why do we have to define an extra container? We could just have one new annotation to specify an annotation type to be repeatable. The reason is that EE libraries use the previous idiom and language designers wanted to give them a straightforward transition to use repeating annotations. It is convenient to simply annotate the existing annotation declarations: the code update required is minor and low risk (nothing is removed).

No comments:

Post a Comment