Annotations in Java

Introduction

Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate. Thus, an annotation leaves the semantics of a program unchanged. However, this information can be used by various tools during both development and deployment.

Annotation Uses

Annotations have a number of uses, among them:

  • Information for the compiler: Annotations can be used by the compiler to detect errors or suppress warnings.
  • Compile-time and deployment-time processing:
    Software tools can process annotation information to generate code, XML files, and so forth.
  • Runtime processing:
    Some annotations are available to be examined at runtime.

Annotation processing is a very powerful mechanism and can be used in a lot of different ways:

  1. to describe constraints or usage of an element: e.g.
     @Deprecated, @Override, or @NotNull
  2. to describe the “nature” of an element, e.g.
     @Entity, @TestCase, @WebService
  3. to describe the behavior of an element:
     @Statefull, @Transaction
  4. to describe how to process the element:
     @Column, @XmlElement


Annotation Basics

An annotation is created through a mechanism based on the interface. For creating custom annotation, @ must precedes the keyword interface. This tells the compiler that an annotation type is being declared. Example:

public @interface SampleAnnotation {
    int intValue();
    String strValue();
}

All annotations consist solely of method declarations. However, you don’t provide bodies for these methods. Instead, Java implements these methods. Moreover, the methods act much like fields.

Many annotations replace comments in code. For an example suppose that a software group traditionally starts the body of every class with comments providing important information:

public class CommonUtil {
    // Author: SANJAY MADNANI
    // Date: 12/07/2013
    // Current revision: 2
    // Last modified: 12/07/2014
    // By: SANJAY MADNANI

    // class code.......
}

To add this same metadata with an annotation, you must first define the annotation type. The syntax for doing this is:

public @interface ClassCommentMetaInfo {
    String author();
    String date();
    int currentRevision() default 1; //optional default values.
    String lastModified() default "N/A"; //optional default values.
    String lastModifiedBy() default "N/A"; //optional default values.
}

After the annotation type is defined, you can use annotations of that type, with the values filled in, like this:

@ClassCommentMetaInfo (
        author = "SANJAY MADNANI",
        date = "12/07/2013",
        currentRevision = 3,
        lastModified = "12/07/2014",
        lastModifiedBy = "SANJAY MADNANI")
public class CommonUtil {
    // class code.......

}

Note:
To make the information in @ClassCommentMetaInfo appear in Javadoc-generated documentation, you must annotate the @ClassCommentMetaInfo definition with the @Documented annotation:

import java.lang.annotation.Documented;

@Documented
public @interface ClassCommentMetaInfo {
// Annotation element definitions
}

When you apply an annotation, you give values to its members. In above example author = “SANJAY MADNANI” is assigning the value in author member of ClassCommentMetaInfo Annotation.

An annotation cannot include extends clause. However, all annotation types automatically extend the Annotation interface. Thus, Annotation is a super-interface of all annotations. It is declared within the java.lang.annotation package. It overrides hashCode( ), equals( ), and toString( ), which are defined by Object. It also specifies annotationType( ), which returns a Class object that represents the invoking annotation. See below Class diagram of Annotation interface:


Any type of declaration can have an annotation associated with it. For example, classes, methods, fields, parameters, and enum constants can be annotated. Even an annotation can be annotated.

Annotation retention policies

A retention policy determines at what point an annotation is discarded. Java defines three such policies, which are encapsulated within the java.lang.annotation.RetentionPolicy enumeration. They are SOURCE, CLASS, and RUNTIME.

  • An annotation with a retention policy of SOURCE is retained only in the source file and is discarded during compilation.
  • An annotation with a retention policy of CLASS is stored in the .class file during compilation. However, it is not available through the JVM during run time.
  • An annotation with a retention policy of RUNTIME is stored in the .class file during compilation and is available through the JVM during run time. Thus, RUNTIME retention offers the greatest annotation persistence.

NOTE: An annotation on a local variable declaration is not retained in the .class file.

A retention policy is specified for an annotation by using one of Java’s built-in annotations: @Retention. Its general form is shown here:

@Retention (RetentionPolicy)
 

Example:

@Retention (RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    public String str();
    public int value();
}

NOTE: If no retention policy is specified for an annotation, then the default policy of CLASS is used.

Obtaining Annotations at Run Time by Use of Reflection

Although annotations are designed mostly for use by other development or deployment tools, if they specify a retention policy of RUNTIME, then they can be queried at run time by any Java program through the use of reflection.

Step 1: Create an annotation with RetentionPolicy.RUNTIME so that it can be used with reflection.

@Retention (RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    public String str();
    public int value();
}

Step 2: We will create a class where we will be using MyAnnotation on few methods (method2 and method5):

public class AnnotatedMethods {
    public void method1() {
        System.out.println("method1");
    }
    @MyAnnotation (str = "method2 of AnnotatedMethod", value = 2)
    public void method2() {
        System.out.println("method2");
    }
    public void method3() {
        System.out.println("method3");
    }
    public void method4() {
        System.out.println("method4");
    }
    @MyAnnotation (str = "method5 of AnnotatedMethod", value = 5)
    public void method5() {
        System.out.println("method5");
    }
}
 

Step 3: Now we shall write a reflection code to invoke all methods which is annotated by MyAnnotation and also we shall retrieve the values of members of MyAnnotation.

public class MyAnnotationTest {
    public static void main(String[] args) throws Exception {
        AnnotatedMethods object = new AnnotatedMethods();
        // get all methods of AnnotatedMethods class
        Method[] methods = object.getClass().getDeclaredMethods();
        for (Method method : methods) {
            // get MyAnnotation annotation of a method
            MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
            if (myAnnotation != null) {
                // invoke annotated methods
                method.invoke(object);
                // retrieve MyAnnotation member values
                System.out.println("str: " + myAnnotation.str() + ", value: " + myAnnotation.value());
            }
        }
    }
}

Below is the output of code:

method2
str: method2 of AnnotatedMethod, value: 2
method5
str: method5 of AnnotatedMethod, value: 5

Marker Annotations

A marker annotation is a special kind of annotation that contains no members. Its sole purpose is to mark an item. Thus, its presence as an annotation is sufficient. The best way to determine if a marker annotation is present is to use the method isAnnotationPresent( ), which is defined by the AnnotatedElement interface.

Here is an example that uses a marker annotation. Because a marker interface contains no members, simply determining whether it is present or absent is sufficient.

@Retention (RetentionPolicy.RUNTIME)
public @interface MyMarker {

}

Single-Member Annotations

A single-member annotation contains only one member. It works like a normal annotation except that it allows a shorthand form of specifying the value of the member. When only one member is present, you can simply specify the value for that member when the annotation is applied—you don’t need to specify the name of the member. However, in order to use this shorthand, the name of the member must be value. Example:

@Retention (RetentionPolicy.RUNTIME)
public @interface SingleMember {
    String value(); // this variable name must be value
}

public class SingleMemberTest {
    @SingleMember ("value")
    public static void test() throws Exception {
        SingleMemberTest test = new SingleMemberTest();
        Method method = test.getClass().getMethod("test");
        SingleMember anno = method.getAnnotation(SingleMember.class);
        System.out.println(anno.value());
    }

    public static void main(String[] args) throws Exception {
        test();
    }

}

Output of the program:
value

Predefined Annotation Types

Click here to Refer Oracle document for Predefined Annotations.

Some Restrictions

There are a number of restrictions that apply to annotation declaration:

  1. No annotation can inherit another.
  2. All methods declared by an annotation must be without parameters. Furthermore, they must return one of the following:
    1. A primitive type, such as int or double
    2. An object of type String or Class
    3. An enum type
    4. Another annotation type
    5. An array of one of the preceding types
  3. Annotation methods cannot specify a throws clause.
Advertisements

About Sanjay Madnani

Software Developer.
This entry was posted in Tutorials and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s