1. Overview
In this tutorial, we'll learn how to get a field's annotations. Additionally, we'll explain how the retention meta-annotation works. Afterward, we'll show the difference between two methods that return a field's annotations.
2. Retention Policy of the Annotation
First, let's have a look at the Retention annotation. It defines the lifecycle of an annotation. This meta-annotation takes a RetentionPolicy attribute. That is to say, the attribute defines the lifecycle where an annotation is visible:
- RetentionPolicy.SOURCE – visible only in the source code
- RetentionPolicy.CLASS – visible to the compiler at compilation time
- RetentionPolicy.RUNTIME – visible to the compiler and to the runtime
Therefore, only the RUNTIME retention policy allows us to read an annotation programmatically.
3. Get a Field's Annotations Using Reflection
Now, let's create an example class with an annotated field. We'll define three annotations, where only two are visible at runtime.
The first annotation is visible at runtime:
@Retention(RetentionPolicy.RUNTIME)
public @interface FirstAnnotation {
}
A second one has the same retention:
@Retention(RetentionPolicy.RUNTIME)
public @interface SecondAnnotation {
}
Finally, let's create a third annotation visible only in source code:
@Retention(RetentionPolicy.SOURCE)
public @interface ThirdAnnotation {
}
Now, let's define a class with a field classMember annotated with all three of our annotations:
public class ClassWithAnnotations {
@FirstAnnotation
@SecondAnnotation
@ThirdAnnotation
private String classMember;
}
After that, let's retrieve all visible annotations at runtime. We'll use Java reflection, which allows us to inspect the field's attributes:
@Test
public void whenCallingGetDeclaredAnnotations_thenOnlyRuntimeAnnotationsAreAvailable() throws NoSuchFieldException {
Field classMemberField = ClassWithAnnotations.class.getDeclaredField("classMember");
Annotation[] annotations = classMemberField.getDeclaredAnnotations();
assertThat(annotations).hasSize(2);
}
As a result, we retrieved only two annotations that are available at runtime. The method getDeclaredAnnotations returns an array of length zero in case no annotations are present on a field.
We can read a superclass field's annotations in the same way: retrieve the superclass's field and call the same getDeclaredAnnotations method.
4. Check if Field Is Annotated With a Specific Type
Let's now look at how to check if a particular annotation is present on a field. The Field class has a method isAnnotationPresent that returns true when an annotation for a specified type is present on the element. Let's test it on our classMember field:
@Test
public void whenCallingIsAnnotationPresent_thenOnlyRuntimeAnnotationsAreAvailable() throws NoSuchFieldException {
Field classMemberField = ClassWithAnnotations.class.getDeclaredField("classMember");
assertThat(classMemberField.isAnnotationPresent(FirstAnnotation.class)).isTrue();
assertThat(classMemberField.isAnnotationPresent(SecondAnnotation.class)).isTrue();
assertThat(classMemberField.isAnnotationPresent(ThirdAnnotation.class)).isFalse();
}
As expected, the ThirdAnnotation is not present because it has a SOURCE retention policy specified for the Retention meta-annotation.
5. Field Methods getAnnotations and getDeclaredAnnnotations
Let's now have a look at two methods exposed by the Field class, getAnnotations and getDeclaredAnnotations. According to Javadoc, the getDeclaredAnnotations method returns annotations that are directly present on the element. On the other hand, Javadoc says for getAnnotations that it returns all annotations present on the element.
A field in a class contains annotations just above its definition. As a result, there is no inheritance of annotations involved at all. All annotations must be defined together with the field definition. Because of that, methods getAnnotations and getDeclaredAnnotations always return the same result.
Let's show it in a simple test:
@Test
public void whenCallingGetDeclaredAnnotationsOrGetAnnotations_thenSameAnnotationsAreReturned() throws NoSuchFieldException {
Field classMemberField = ClassWithAnnotations.class.getDeclaredField("classMember");
Annotation[] declaredAnnotations = classMemberField.getDeclaredAnnotations();
Annotation[] annotations = classMemberField.getAnnotations();
assertThat(declaredAnnotations).containsExactly(annotations);
}
Moreover, in the Field class, we can find that the getAnnotations method calls the getDeclaredAnnotations method:
@Override
public Annotation[] getAnnotations() {
return getDeclaredAnnotations();
}
6. Conclusion
In this short article, we explained the retention policy meta-annotation role in retrieving annotations. Then we showed how to read a field's annotations. Finally, we proved that there is no inheritance of annotations for a field.
As always, the source code of the example is available over on GitHub.