Annotations in Java

Annotations are used to add metadata information to the Java Elements. Like classes, interfaces or enums, Annotations define a type in Java programming and they can be applied to several Java Elements such as fields, methods, constructors, classes etc. The software tools which will read and interpret the Annotations will implement a lot of functionalities from the metadata information received. For example, they can ensure the consistency between classes, can check the validity of the parameters passed by the programmers at runtime and can generate lot of base code for a Java based project. This section provides us information on the various aspects of Annotations.

Java SE 5 introduced Annotations and now it is being used in Java EE based frameworks like Spring and Hibernate to a great extent. Basically, Annotations provides additional information about the Java Elements, so it is an alternative option for extensible Markup Language (XML) and Java marker interfaces.

Java Annotations are basically used for the following purposes:

1. Compiler instructions: The built-in annotations available in Java such as @Override, @Deprecated, and @SuppressWarnings that can provide certain instructions to the compiler. For instance, @Override is used to instruct the interpreter that the annotated method is being overridden. 

2. Build-time instructions: Annotations provide build-time / compile-time instructions to the compiler that are used by software build tools for generating codes, XML files etc.

3. Runtime instructions: Annotations can be defined at the runtime so that they could be made available to access in the runtime using Java Reflection API and can be used to give instructions to the program.

Let us consider the following class definition:

Employee.java
final class Employee  {               
      private int id;               
      private String name;
      private double salary;               
      // Setters and Getters for Employee class.        
}

The above denotes the definition of a class named Employee. We can see that the class declaration is preceded with the final keyword which tells the compiler that the class cannot be sub classed. So, the introduction of the final keyword adds some additional information (or constraints) over the class definition informing that no other class can extend the Employee class.

Therefore, the final keyword forms a part in specifying metadata information in the class definition. So, this is one variation of Annotation. Annotations are generally a way to add metadata information to an element (an element can be a class, method, field, or anything) and these metadata information are processed by the tools (compilers, javadoc etc.). Annotations are differentiated from other elements like classes, interfaces etc., by preceding an ‘@‘ symbol before it.

An annotation definition looks like the following:

TestAnnotation.java
@interface ExampleAnnotation {                
    // Property definition here.        
}

We should not get confused with the interface keyword here. It has nothing to do with the annotations. The symbol ‘@‘ along with interface is the start of the annotation definition and ExampleAnnotation in the above case is the name of the Annotation. Whether annotations can be applied to a class (class-level annotation), or a method (method-level annotation) or a field (field-level annotation) is specified in the declaration of the annotation itself. This is referred to as Annotating an Annotation itself. 

 

Features of Annotations

Some important features of Java Annotations are:

  • Java Annotations begin with ‘@’ and they provide metadata information to Java elements.
  • Annotation methods cannot have parameters.
  • Annotation methods return types are limited to primitives, String, Enums, Annotation or array of these.
  • Annotation methods can have default values as well.
  • Annotations cannot include any extends clause.
  • Annotations can have meta annotations attached to them. They provide information about the annotation.

 

Types of Annotations

There are mainly three types of Annotations in Java:—

1. Marker Annotations:
The objective is to mark a declaration which describes their presence. They do not contain any members. Thus, its presence as an annotation is just adequate. @Override is an example of Marker Annotation.

Example.

@interface SampleAnnotation  {  }

2. Single value Annotations:
These annotations consist of only one member and allow a shorthand form of specifying the value of the member.

Example.

@interface SampleAnnotation  {  
int value() default 10;
}

3. Full Annotations:
These annotations contains multiple data members or name, value, pairs.

Example.

@interface SampleAnnotation {
int value1() default 20;
double value2() default 3.142;
String value3() default "abc"
}
 

 

Built-in Annotations

Java provides seven built-in annotations. Some of them are applied to Java code and some to other annotations (called meta annotations).

I. Built-In Java Annotations used in Java code:
  • @Override
  • @SuppressWarnings
  • @Deprecated
II. Built-In Java Annotations used as Meta Annotations:
  • @Target
  • @Retention
  • @Inherited
  • @Documented

After understanding these annotations, we can build “Custom Annotation” according to our requirements.

 

I. Built-In Annotations used in Java code

There are three types of annotations (part of java.lang package) to be used in Java code:

@Override Annotation
It is a marker annotation that can be used only on methods. A method annotated with @Override must override a method from a superclass. If it doesn’t, a compile-time error will result. It is used to ensure that a superclass method is actually overridden in its subclass. See the coding example below.

TestAnnotation01.java

class Super {
public void printMessage() {
System.out.println("From Superclass");
}
}

class Sub extends Super {
@Override
public void printMessage(String msg) { //we should use printMessage()
System.out.println("From Subclass");
}
}

public class TestAnnotation01 {
public static void main(String[] args) {
Super s = new Sub();
s.printMessage();
}
}

Output:

C:\testing>javac TestAnnotation01.java
Compilation Error: method does not override or implement a method from a supertype

NOTE: If we remove parameter (String msg) or we remove @Override, the program compiles with success.

 

@SuppressWarnings Annotation

It instructs the compiler to suppress the compile time warnings specified in the annotation parameters. Any unchecked warning is generated when a legacy code interfaces with a code that use generics. See the coding example below.

TestAnnotation02.java

import java.util.ArrayList;

public class TestAnnotation02 {
@SuppressWarnings("unchecked")

public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("Pen");
list.add("Pencil");
list.add("Eraser");
list.add("Book");
list.add("Diary");
list.add("Notebook");

System.out.println("LIST = " + list);
}
}

Output:

C:\testing>java TestAnnotation02
LIST = [Pen, Pencil, Eraser, Book, Diary, Notebook]

NOTE: If we remove the @SuppressWarnings(“unchecked”) annotation, it will show the following warning message at compile time because we are now using non-generic collection:

Note: TestAnnotation02.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

 

@Deprecated Annotation
It is a marker annotation which indicates that a declaration is obsolete and has been replaced by a newer form. The compiler generates a warning whenever a program uses a method, class, or field that has already been marked with the @Deprecated annotation.

Keep in mind that when an element is deprecated, it should also be documented using the javadoc @deprecated tag, as shown in the following example. Make a note of case difference with this tag and @Deprecated. The @deprecated tag is used for documentation purpose and it has got higher priority than @Deprecated annotation when both are used together in a program.

See the coding example below.

TestAnnotation03.java

/**
* @deprecated
* reason for why it was deprecated
*/
class Example {

@Deprecated
void old() {
System.out.println("Hi old!");
}

void young() {
System.out.println("Hi young!");
}
}

public class TestAnnotation03 {

public static void main(String args[]) {

Example ex = new Example();
ex.old();
ex.young();
}
}

Output:

C:\testing>javac TestAnnotation03.java
Note: TestAnnotation03.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

C:\testing>java TestAnnotation03
Hi old!
Hi young!

 

II. Built-In Annotations used as Meta Annotations

There are four types of meta annotations (part of java.lang.annotation package):

  1. Documented – It denotes that elements using this annotation should be documented by javadoc and similar tools. This type should be used to annotate the declarations of types whose annotations affect the use of annotated elements by their clients. If a type declaration is annotated with Documented, its annotations become part of the public API of the annotated elements.
  2. @Retention – It denotes how long annotations with the annotated type are to be retained. It takes RetentionPolicy argument whose Possible values are SOURCE, CLASS and RUNTIME.
  3. @Target – It indicates the kinds of program element to which an annotation type is applicable. Some possible values are TYPE, METHOD, CONSTRUCTOR, FIELD etc. If Target meta-annotation is not present, then annotation can be used on any program element.
  4. @Inherited – It points that an annotation type is automatically inherited. If user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class’s superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found, or the top of the class hierarchy (Object) is reached.

 

Custom Annotation

In this section, we shall see how to create custom annotations. It is quite similar to writing an interface, except that the interface keyword is prefixed with @ symbol. We can declare methods in annotation. 

Some key points are mentioned below:

  • Annotations are created by using @interface, followed by annotation name as shown in the below example.
  • An annotation can have elements as well. They look like methods. We should not provide implementation for these elements.
  • All annotations extends java.lang.annotation.Annotation interface. Annotations cannot include any extends clause.

Syntax of Declaration:-

[Access Specifier] @interface <annotation_name>
{         
   data_type <method_name>() [default value];
}
  • The identifier is here annotation_name.
  • Parameter should not be associated with method declarations and throws clause should not be used with method declaration.
  • Parameters will not have a null value but can have a default value.
  • Access Specifier or default value is optional.
  • Return type of method should be either primitive, enum, string, class name or array of primitive, enum, string or class name type.

 

An Example

For example in the code below, we have four elements for the custom annotation named ExampleAnnotation. They look like methods and we should not provide implementation for these elements. We have applied here all types of meta annotations. We are now setting the values for the remaining elements of this annotation in Testing class which is also having a method named message(). Finally, we have used Java Reflection API to access all the elements of the annotation from main class CustomAnnotation. See the code below.

import java.lang.annotation.*;
import java.lang.reflect.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
@interface ExampleAnnotation {
String name();
int age() default 30;
String city() default "Delhi";
double salary() ;
}

class Testing {
// default values can be skipped
@ExampleAnnotation(name = "Ramesh", salary = 9999.95)
public void message() {
System.out.println("Annotation Example!");
}
}

public class CustomAnnotation {

public static void main(String[] args) {

try {
Testing t = new Testing();
Method me = t.getClass().getMethod("message");

ExampleAnnotation ex = me.getAnnotation(ExampleAnnotation.class);
System.out.println("Name : " + ex.name());
System.out.println("Age : " + ex.age());
System.out.println("City : " + ex.city());
System.out.println("Salary : " + ex.salary());
}
catch(Exception e) {
e.getMessage();
}

}
}

As you can see, we have not given any value to the name() and salary() elements as it is optional to set the values of these elements (default values already been set in Annotation definition, but if we want we can assign new value). However we have to provide the values of other elements (the elements that do not have default values set) while using annotation.

NOTE: All the elements that have default values set while creating annotations can be skipped while using annotation.

Output:

C:\testing>java CustomAnnotation
Name : Ramesh
Age : 30
City : Delhi
Salary : 9999.95

 

Θ Hope you have liked my article! Θ