Tuesday, September 11, 2012

Java Annotations - Retention

Consider a Java annotation:

public @interface AnAnnotaton {


A class with this annotation applied on it:

class AnAnnotatedClass{

And a test which checks if this annotation is present on a class:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

import java.lang.annotation.Annotation;
import org.junit.Test;

public class AnAnnotationTest {
 public void testAnAnnotation() throws Exception {
  AnAnnotatedClass anAnnotatedClass = new AnAnnotatedClass();
  Annotation[] annotationsOnClass = anAnnotatedClass.getClass().getAnnotations();
  assertThat(annotationsOnClass.length, is(1));


Sounds reasonable right, one would expect the above test to pass since the class does have an annotation of AnAnnotation on it.

But, this test fails, and the reason is...

a missing meta annotation(@Retention) on the annotation which indicates how long the annotation is to be retained, if the annotation above is changed as follows, the test would work as expected.

public @interface AnAnnotaton {


So what does @Retention do - to quote from the Javadoc:

Indicates how long annotations with the annotated type are to be retained. If no Retention annotation is present on an annotation type declaration, the retention policy defaults to RetentionPolicy.CLASS

there are three different retention policies:
1. SOURCE - where the annotations are removed by the compiler
2. CLASS - Annotations are present in the bytecode, but are not present at runtime and hence not available when trying to reflectively determine if a class has the annotation.
3. RUNTIME - Annotations are retained in the byte code and is available at runtime and hence can be reflectively found on a class.

This is the reason why when the annotation definition was changed to include @Retention(RetentionPolicy.RUNTIME), the test now runs through.

Something basic, but easy to miss out.

No comments:

Post a Comment