Java 8 introduced an elegant feature called repeatable annotations, which simplified how annotations are used in Java, especially when you need to apply the same annotation to a declaration multiple times. This feature is particularly useful for developers dealing with scheduling tasks or applying configurations where multiple instances of the same type of data are required.
The Problem Before Java 8
Prior to Java 8, if you needed to annotate a method with multiple instances of the same annotation, you had to create a wrapper annotation. For example, scheduling a method to run at different times required a container annotation:
@Schedules({
@Schedule(dayOfMonth="last"),
@Schedule(dayOfWeek="Fri", hour="23")
})
public void doPeriodicCleanup() { ... }
This approach was cumbersome and less readable, especially with more complex scheduling.
The Solution with Java 8: Repeatable Annotations
Java 8 allows the same annotation to be repeated on a single method, reducing the need for a wrapper annotation. This change makes code more readable and concise.
Example of Using Repeatable Annotations
Consider a method that needs to be scheduled to run on the last day of the month and every Friday at 11 PM:
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }
This method now directly shows its scheduling without the extra clutter of a container annotation.
How It Works
- Declare a Repeatable Annotation Type:
First, define the annotation type as repeatable using the@Repeatable
meta-annotation:
import java.lang.annotation.Repeatable;
@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}
@Repeatable(Schedules.class)
tells the compiler that Schedule
can be repeated, and Schedules
is the container that will hold all occurrences of Schedule
.
- Declare the Container Annotation Type:
The container annotation,Schedules
, must hold an array of the repeatable annotation:
public @interface Schedules {
Schedule[] value();
}
Retrieving Annotations
Annotations can be retrieved using the Reflection API. Methods like AnnotatedElement.getAnnotationsByType(Class<T>)
allow for easy access to all annotations of a specified type, even if they are used repeatedly:
Schedule[] schedules = this.getClass().getMethod("doPeriodicCleanup").getAnnotationsByType(Schedule.class);
Design Considerations
When designing a repeatable annotation, consider where it can be used (methods, fields, etc.) and its cardinality (zero, one, or more times). Proper design can make an annotation flexible and powerful, enhancing its utility.
Conclusion
Java’s repeatable annotations provide a cleaner, more intuitive approach to applying the same annotation multiple times. This feature not only makes code more readable but also keeps it organized without redundant container annotations. As Java continues to evolve, features like these significantly enhance the language’s usability and flexibility.
Reference : https://docs.oracle.com/javase/tutorial/java/annotations/repeating.html