Explore Annotations in Java 8
Explore the evolution of annotations in Java 8 and how they are being used today!
Join the DZone community and get the full member experience.
Join For FreeAnnotations were introduced back in Java SE 1.5, and the purpose of the Java annotation was to allow programmers to write metadata about their program. As per the Oracle Docs, the definition of an annotation is: "annotations, a form of metadata, provide data about a program that is not part of the program itself."
Annotations can be used anywhere in your code, i.e. within classes, methods, and variables. From Java 8, it can be used in type declaration as well.
You may also like: Creating Annotations in Java
Annotated code does not have any direct relation to the program. It is only information to other programs or JVM who can use this information for their purpose.
Annotation Syntax
An annotation is declared using the character @
and the annotation name, i.e. @AnnotationName
. When the compiler goes through this element, it understands that this is an annotation. For example:
@ExampleAnnotation
public class SampleClass {
}
The annotation above is called ExampleAnnotation
and it is annotating the class SampleClass
.
An annotation may have properties. These are given in key-value pairs when declaring the annotations. For example:
@ExampleAnnotation(name = ”first name”, age = 35)
public void simpleMethod() {
}
Note that here, the ExampleAnnotation
is annotating a method. If an annotation has only one property, then the name of the property can be skipped when declaring the annotation. Below is an example:
@ExampleAnnotation(“I am the only property”)
public void simpleMethod() {
}
Multiple annotations are possible for an element. Check it out:
@Annotation1
@Annotation2(“Another Annotation”)
public class SimpleClass {
}
Form J2SE 8; the same annotations can be used multiple times to an element, for example:
@ExampleAnnotation(“Annotation used”)
@ExampleAnnotation(“Annotation repeated”)
public class SimpleClass {
}
This will be discussed in detail in the @Repeatable
annotation section.
Predefined Annotations in Java
Java comes with a set of predefined annotations. The annotations available in Java Core are explained below:
@Retention
: This annotation annotates other annotations and indicates the scope of the annotated annotations. Some possible values are:
-
SOURCE
— indicates that this annotation is available only in the source code and ignored by the Compiler and JVM, and hence not available in runtime. -
CLASS
— indicates that this annotation is available to the Compiler but not JVM, and hence not available during runtime. -
RUNTIME
— indicates that the annotation is available to JVM, and hence can be used in runtime.
@Target
: This annotation indicates the target elements an annotation can be applied to:
ANNOTATION_TYPE
— means that the annotation can be applied to other annotations.CONSTRUCTOR
— can be applied to a constructor.FIELD
— can be applied to a field or property.LOCAL_VARIABLE
— can be applied to a local variable.METHOD
— can be applied to a method.PACKAGE
— can be applied to a package declaration.PARAMETER
— can be applied to the parameters of a method.TYPE
— can be applied toClass
,Interface
,Annotation
, or enum declaration.PACKAGE
— can be applied to package declaration.TYPE_PARAMETER
— can be applied to the type parameter declaration.TYPE_USE
— can be applied to any type
@Documented
: This annotation can be applied to other annotations. It means that the annotated elements will be documented using the Javadoc tool.
@Inherited
: By default, annotations are not inherited by subclasses. But if an annotation is marked as @Inherited
, that means when a class is annotated with that annotation, the annotation is also inherited by subclasses. This annotation is applicable only for class. Note that if an interface is annotated with that annotation, the annotation is not inherited by implementing classes.
@Deprecated
: Indicates that the annotated element should not be used. This annotation gets the compiler to generate a warning message. It can be applied to methods, classes, and fields.
@SuppressWarnings
: Indicates the compiler not to produce warnings for a specific reason or reasons.
@Override
: This annotation informs the compiler that the element is overriding an element of the superclass. It is not mandatory to use when overriding elements, but it helps the compiler to generate errors when the overriding is not done correctly, for example, if the subclass method parameters are different than the superclass ones, or if the return type does not match.
@SafeVarargs
: Asserts that the code of the method or constructor does not perform unsafe operations on its arguments.
@Repeatable Annotation
This annotation indicates that an annotation annotated with this one can be applied more than once to the same element.
This concept can be more clearly understood with the help of an example.
To use this annotation, first, we need to define an annotation, which can be used to annotate repeatedly to a class.
@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.TYPE_USE)
@Repeatable (RepeatableAnnotationContainer.class)
public @interface RepeatableAnnotation() {
String values();
}
Here, RepeatableAnnotation
is an annotation that can be used repeatedly to annotate an element.
Next, we need to define the RepeatableAnnotationContainer
annotation type. This is basically a container of the annotation type, and it must have an array of the RepeatableAnnotation
annotation type.
public @interface RepeatableAnnotationContainer {
RepeatableAnnotation [] value();
}
Now, the Repeatable
annotation can be used multiple times to annotate any element.
@RepeatableAnnotation (“I am annotating the class”)
@RepeatableAnnotation (“I am annotating the class again”)
@RepeatableAnnotation (“I am annotating the class for the third time”)
public class RepeatedAnnotationExample {
}
Next, to retrieve the value of the annotation in the program, the arrays of the container will be retrieved first. Each element of the array will contain one value. For example:
@RepeatableAnnotation (“I am annotating the class”)
@RepeatableAnnotation (“I am annotating the class again”)
@RepeatableAnnotation(“I am annotating the class for the third time”)
public class RepeatableAnnotationExample {
public static void main(String [] args) {
Class object = RepeatableAnnotationExample.class
Annotation[] annotations = object.getAnnotations();
for (Annotation annotation : annotations) {
RepeatableAnnotationContainer rac = (RepeatableAnnotationContainer) annotation;
RepeatableAnnotation [] raArray = rac.value();
for (RepeatableAnnotation ra : raArray) {
System.out.println(ra.value);
}
}
}
}
When the above code is executed, the output will be:
I am annotating the class
I am annotating the class again
I am annotating the class for the third time.
Type Annotations
After the release of Java 8, annotations can be applied to any type of use. This means that annotations can be used anywhere we use a type. As an example, when creating a class instance using a new operator, typecasting, when implementing an interface using an implements clause, throws a clause, etc., this form of annotation is called a type annotation.
The purpose of this type of annotation is to support improved analysis of Java programs and ensure stronger type checking. Up to the Java 8 release, Java contained no type-checking framework, but using the type annotation, a type checking framework can be written and used in java program.
As an example, suppose we want a particular variable to never be assigned null throughout our program. We can write a custom plugin NonNull
to check this and annotate that particular variable with that custom annotation. The variable declaration should then be:
@NonNull String notNullString;
When the code is compiled, the compiler checks for potential problems and raised warnings when any such code is found where the variable may be assigned a null value.
Custom Annotations
Java allows programmers to define and implement custom annotations. The syntax to define custom annotations is:
public @interface CustomAnnotation { }
This creates a new annotation type called CustomAnnotation
. The @interface
keyword is used to define a custom annotation.
When defining custom annotations, two mandatory attributes must be defined for the annotation. Other attributes can be defined here, but these two are important and mandatory. These two attributes are the Retention Policy and Target.
These two attributes are declared in the form of annotations to that custom annotation. Also, properties to the annotations can be defined when defining the custom annotation. For example:
@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.ELEMENT)
public @interface CustomAnnotation {
public String name() default “Mr Bean”;
public String dateOfBirth();
}
In the above custom annotation, the Retention Policy is RUNTIME
, that means it is available to the JVM at runtime and the Target is ELEMENT
, which means it can be annotated to any element type.
Also, it has two properties: name with the default value Mr Bean
and one dateOfBirth
with no default value.
Note that the properties declared as Method
don’t have any parameter and throws clause. Also, the return type is restricted to String, class, enums, and annotations and arrays of the mentioned return types.
Now, we can use our custom annotation in the following way:
@CustomAnnotation (dateOfBirth = “1980-06-25”)
public class CustomAnnotatedClass {
}
Similarly, a custom annotation for methods can be created using the @Target
( ElementType.METHOD
) annotation and can be used to annotate any method.
Retrieving Annotations and its Properties
The Java Reflection API contains several methods that can be used to retrieve in runtime annotations from classes, methods, and other elements.
The interface that contains all of these methods is the AnnotatedElement
. The most important ones are:
getAnnotations()
: Returns all annotations for the given element, also the ones that are not explicitly defined in the element definition.isAnnotationPresent(annotation)
: Checks if the passed annotation is available or not in the current element.getAnnotation(class)
: Retrieves a specific annotation passed as a parameter. Returns null if this annotation is not present for the given element.
This class is implemented by java.lang.Class
, java.lang.reflect.Method
, and java.lang.reflect.Field
, among others, so it can be used basically with any kind of Java element.
The following program demonstrates how to get information about our defined custom annotation:
public static void main(String [] args) {
Class object = CustomAnnotatedClass.class;
// Retrieve all annotations from the class
Annotation[] annotations = object.getAnnotations();
for( Annotation annotation : annotations ) {
System.out.println(annotation);
}
// Checks if an annotation is present
if( object.isAnnotationPresent( CustomAnnotationClass.class ) ) {
// Gets the desired annotation
Annotation annotation = object.getAnnotation(CustomAnnotationClass.class) ;
System.out.println(annotation);
}
// fetch the attributes of the annotation
for(Annotation annotation : annotations) {
System.out.println(“name: “ + annotation.name());
System.out.println(“Date of Birth: “+ annotation.dateOfBirth());
}
// the same for all methods of the class
for( Method method : object.getDeclaredMethods() ) {
if( method.isAnnotationPresent( CustomAnnotationMethod.class ) ) {
Annotation annotation = method.getAnnotation(CustomAnnotationMethod.class );
System.out.println( annotation );
}
}
}
Conclusion
Annotations gradually became an essential part of developing any enterprise application using the J2EE stack. Nowadays, almost all popular libraries use annotations for different purposes, like code quality analysis, unit testing, XML parsing, dependency injection, and others. A few of the popular libraries that use annotations extensively are Junit, Hibernate, Spring MVC, Findbugs, JAXB, JUnit.
Further Reading
Opinions expressed by DZone contributors are their own.
Comments