Repeating and Inherited Annotations


Repeating Annotations

Until Java 8, two instances of the same annotation could not be applied to a single element. The standard workaround was to use a container annotation holding an array of some other annotation:

// Author.java
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
	String value();
}
 
// Authors.java
@Retention(RetentionPolicy.RUNTIME)
public @interface Authors {
	Author[] value();
}
 
// Test.java
@Authors({
	@Author("Mary"),
	@Author("Sam")
})
public class Test {
	public static void main(String[] args) {
		Author[] authors = Test.class.getAnnotation(Authors.class).value();
		for (Author author : authors) {
			System.out.println(author.value());
			// Output:
			// Mary
			// Sam
		}
	}
}
Version ≥ Java SE 8

Java 8 provides a cleaner, more transparent way of using container annotations, using the @Repeatable annotation. First we add this to the Author class:

@Repeatable(Authors.class)

This tells Java to treat multiple @Author annotations as though they were surrounded by the @Authors container. We can also use Class.getAnnotationsByType() to access the @Author array by its own class, instead of through its container:

@Author("Mary")
@Author("Sam")
public class Test {
	public static void main(String[] args) {
		Author[] authors = Test.class.getAnnotationsByType(Author.class);
		for (Author author : authors) {
			System.out.println(author.value());
			// Output:
			// Mary
			// Sam
		}
	}
}

Inherited Annotations

By default class annotations do not apply to types extending them. This can be changed by adding the @Inherited annotation to the annotation definition

Example

Consider the following 2 Annotations:

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

and

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

If three classes are annotated like this:

@UninheritedAnnotationType
class A {
}
 
@InheritedAnnotationType
class B extends A {
}
 
class C extends B {
}

running this code

System.out.println(new A().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println(new B().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println(new C().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println("_________________________________");
System.out.println(new A().getClass().getAnnotation(UninheritedAnnotationType.class));
System.out.println(new B().getClass().getAnnotation(UninheritedAnnotationType.class));
System.out.println(new C().getClass().getAnnotation(UninheritedAnnotationType.class));

will print a result similar to this (depending on the packages of the annotation):

null
@InheritedAnnotationType()
@InheritedAnnotationType()
_________________________________
@UninheritedAnnotationType()
null
null

Note that annotations can only be inherited from classes, not interfaces.

Basic Programs