You are on page 1of 31

Annotations

Annotations provide data about a program that is not part of the program itself. They have no direct effect on the operation of the code they annotate. Annotations have a number of uses, among them:
y

Information for the compiler Annotations can be used by the compiler to detect errors or suppress warnings. Compiler-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.

Annotations can be applied to a program's declarations of classes, fields, methods, and other program elements. The annotation appears first, often (by convention) on its own line, and may include elements with named or unnamed values: @Author( name = "Benjamin Franklin", date = "3/27/2003" ) class MyClass() { } or @SuppressWarnings(value = "unchecked") void myMethod() { } If there is just one element named "value," then the name may be omitted, as in: @SuppressWarnings("unchecked") void myMethod() { } Also, if an annotation has no elements, the parentheses may be omitted, as in: @Override

void mySuperMethod() { } Documentation Many annotations replace what would otherwise have been comments in code. Suppose that a software group has traditionally begun the body of every class with comments providing important information: public class Generation2List { // // // // // // Generation3List extends

Author: John Doe Date: 3/17/2002 Current revision: 6 Last modified: 4/12/2004 By: Jane Doe Reviewers: Alice, Bill, Cindy

// class code goes here } To add this same metadata with an annotation, you must first define the annotation type. The syntax for doing this is: @interface ClassPreamble { String author(); String date(); int currentRevision() default 1; String lastModified() default "N/A"; String lastModifiedBy() default "N/A"; String[] reviewers(); // Note use of array } The annotation type definition looks somewhat like an interface definition where the keyword interface is preceded by the @ character (@ = "AT" as in Annotation Type). Annotation types are, in

fact, a form of interface, which will be covered in a later lesson. For the moment, you do not need to understand interfaces. The body of the annotation definition above contains annotation type element declarations, which look a lot like methods. Note that they may define optional default values. Once the annotation type has been defined, you can use annotations of that type, with the values filled in, like this: @ClassPreamble ( author = "John Doe", date = "3/17/2002", currentRevision = 6, lastModified = "4/12/2004", lastModifiedBy = "Jane Doe", reviewers = {"Alice", "Bob", "Cindy"} // Note array notation ) public class Generation3List extends Generation2List { // class code goes here } Note: To make the information in @ClassPreamble appear in Javadoc-generated documentation, you must annotate the @ClassPreamble definition itself with the @Documented annotation: import java.lang.annotation.*; // import this to use @Documented @Documented @interface ClassPreamble {

// Annotation element definitions } Annotations Used by the Compiler There are three annotation types that are predefined by the language specification itself: @Deprecated, @Override, and @SuppressWarnings. @Deprecatedthe @Deprecated annotation indicates that the marked element is deprecated and should no longer be used. The compiler generates a warning whenever a program uses a method, class, or field with the @Deprecated annotation. When an element is deprecated, it should also be documented using the Javadoc @deprecated tag, as shown in the following example. The use of the "@" symbol in both Javadoc comments and in annotations is not coincidentalthey are related conceptually. Also, note that the Javadoc tag starts with a lowercase "d" and the annotation starts with an uppercase "D". // Javadoc comment follows /** * @deprecated * explanation of why it was deprecated */ @Deprecated static void deprecatedMethod() { } } @Overridethe @Override annotation informs the compiler that the element is meant to override an element declared in a superclass (overriding methods will be discussed in the the lesson titled "Interfaces and Inheritance").

// mark method as a superclass method // that has been overridden @Override int overriddenMethod() { } While it's not required to use this annotation when overriding a method, it helps to prevent errors. If a method marked with @Override fails to correctly override a method in one of its superclasses, the compiler generates an error. @SuppressWarningsthe @SuppressWarnings annotation tells the compiler to suppress specific warnings that it would otherwise generate. In the example below, a deprecated method is used and the compiler would normally generate a warning. In this case, however, the annotation causes the warning to be suppressed. // use a deprecated method and tell // compiler not to generate a warning @SuppressWarnings("deprecation") void useDeprecatedMethod() { objectOne.deprecatedMethod(); //deprecation warning - suppressed } Every compiler warning belongs to a category. The Java Language Specification lists two categories: "deprecation" and "unchecked." The "unchecked" warning can occur when interfacing with legacy code written before the advent of generics (discussed in the lesson titled "Generics"). To suppress more than one category of warnings, use the following syntax: @SuppressWarnings({"unchecked", "deprecation"}) Annotation Processing The more advanced uses of annotations include writing an annotation processor that can read a Java program and take actions based on its

annotations. It might, for example, generate auxiliary source code, relieving the programmer of having to create boilerplate code that always follows predictable patterns. To facilitate this task, release 5.0 of the JDK includes an annotation processing tool, called apt. In release 6 of the JDK, the functionality of apt is a standard part of the Java compiler. To make annotation information available at runtime, the annotation type itself must be annotated with @Retention(RetentionPolicy.RUNTIME), as follows: import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @interface AnnotationForRuntime { // Elements that give information // for runtime processing }

) Introduction Annotations in Java is all about adding meta-data facility to the Java Elements. Like Classes, Interfaces or Enums, Annotations define a type in Java and they can be applied to several Java Elements. Tools which will read and interpret the Annotations will implement a lot of functionalities from the meta-information obtained. For example, they can ensure the consistency between classes, can check the validity of the paramters passed by the clients at run-time and can generate lot of base code for a project. This article provides you a complete guide detailing the various aspects of Annotations. The topics covered in this article are as follows,
y y y y

Page - 1Introduction to Java 5.0 Annotations Page - 2 Built-in Annotations in Java Page - 3User-defined Annotations Discuss Here

2) Java Annotations 2.1) Introduction Java, being a wonderful object-oriented language provided support for Annotations starting from 5.0. This feature was added to Java 5.0 as a result of the JSR 175 namely "A Metadata Facility for the JavaTM Programming Language". Annotations in Java can be seen elsewhere in a program. It can be seen in class declaration, method declaration, field declaration etc. The added Annotation to Java Elements have proven

themselves to be considerably useful in many instances. Consider the following class definition, Employee.java final class Employee { private String name; private String id; // class. } The above is a definition of a class, Employee. It can be noted that the class declaration is preceded with the final keyword which tells that this class cannot be sub-classed. So, the introduction of the final keyword adds some additional information (or adds some constraints) over the class definition telling that no other class in the word can extend the Employee class. Therefore, the final keyword forms a part in providing meta-data 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 meta-data are processed by the tools (compilers, javadoc, etc.). Annotations are differentiated from other elements like class, interface etc., by preceding an '@' symbol before it. An annotation definition looks like the following, TestAnnotation.java public @interface TestAnnotation { Getters and setters for Employee

// Property Definition here. } Don't get confused with the interface keyword. It has nothing to do with annotations. '@' along with interface is the start of the annotation definition and TestAnnotation in the above case is the name of the Annotation. Whether annotations can be applied to class (a 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. The Meta-Annotations that would be covered in the forthcoming sections are,
y y

Target Annotation Retention Annotation

2.2) Target Annotation For example, if the case is that @TestAnnotation annotation can only be applied to methods, then there is a Meta-Annotation (meta-data about meta-data) which tells for which element type this annotation is applicable. For example, the following is the declaration of the @TestAnnotation annotation along with some meta-data that states the elements that this annotation can be applied to. TestAnnotation.java @Target(ElementType.METHOD) public @interface TestAnnotation { // Property Definitions here. }

From the above, we can see that the Annotation @TestAnnotation is annotated with @Target. This kind of Annotation Chaining is always possible. The target element tells that the @TestAnnotation annotation can be applied only to methods and not to any other element types. The argument to @Target Annotation can be one from the possible set of values of any Java Element, which is defined in a well-defined Enum called ElementType. Here are the possible values taken by this Enum,
y

y y

y y

TYPE Applied only to Type. A Type can be a Java class or interface or an Enum or even an Annotation. FIELD Applied only to Java Fields (Objects, Instance or Static, declared at class level). METHOD Applied only to methods. PARAMETER Applied only to method parameters in a method definition. CONSTRUCTOR Can be applicable only to a constructor of a class. LOCAL_VARIABLE Can be applicable only to Local variables. (Variables that are declared within a method or a block of code). ANNOTATION_TYPE Applied only to Annotation Types. PACKAGE Applicable only to a Package.

2.3) Retention Annotation Another commonly used Meta-data for an Annotation is the Retention Policy. Assume that we have some Annotations defined in the source code. We have a mechanism through which we can say that to what extent the Annotations should be retained. The three possible ways of telling this are,
y y

Retain the Annotation in the Source Code only Retain the Annotation in the Class file also.

Retain the Annotation Definition during the Run-time so that JVM can make use of it.

The Annotation that is used to achieve this is @Retention and it takes a possible values of SOURCE, CLASS and RUNTIME defined in RetentionPolicy Enumeration. For example, if we want to retain the @TestAnnotation information till the class file, we can define something like this, TestAnnotation.java @Target(ElementType.METHOD) @Retention(RetentionPolicy.CLASS) public @interface TestAnnotation { // Property Definitions here. }

3) Built-in Annotations in Java There are some pre-defined annotations available in the Java Programming language. They are,
y y

Override Deprecated

SuppressWarnings

3.1) The @Override Annotation The syntax of the @Override annotation is as follows, Override.java @Retention(RetentionPolicy.CLASS) @Target(ElementType.RUNTIME) public @interface Override { } @Override annotation essentially tells to the compiler that, whenever such an annotation is defined in a method of some class, then that method must be an overridden method. If not, then the compilers can report them as errors. Consider the following sample which explains this, Employee.java public class Employee { protected void startWork() { // Code that will start to do some work. } protected void endWork() { // Code to end the work.

} } Manager.java public class Manager extends Employee { @Override protected void startWork() { // Code that will start to do some work. } @Override protected void endWork() { // Code to end the work. } } In the above sample code, we have a class called Employee with two methods namely startWork() and endWork(). These methods have been marked as protected so that sub-classses can override them by providing their own functionality. A class, Manager is created and the methods are overriden in the sub-class. At this point, we have to note one key issue. The methods startWork() and endWork() has been annotated with @Override annotation. This tells to the compiler that the method that is annotated with @Override is an overridden method. So, the compiler immediately climbs up to the base class to find

the existence of such a method. If no such method is found in the base class, then the compiler can report an error message. The major aim of introducing this annotation @Override is to catch the errors that programmers might make when overriding a method in the sub-class. For example, consider the following sample code, DefaultTableCellEditor.java public class DefaultTableCellEditor implements TableCellEditor { public Component getTableCellEditorComponent (JTable table, Object value, boolean isSelected, int row, int column) { // here. } } If the functionality of the getTableCellEditorComponent() has to be overriden, then a new class has to be created by extending the DefaultTableCellEditor as shown below, MyCustomizedTableCellEditor.java Default functionality goes

public class MyCustomizedTableCellEditor extends DefaultTableCellEditor { public Component getTableCellEditorComponet(JTable table, Object value, boolean isSelected, int row, int column){ // goes here. } } The above code compiles and run fine, but not with the expected behavior because of the fact that when re-defining the method in the sub-class the method name has been wrongly spelled (getTableCellEditorComponet() instead of getTableCellEditorComponent()). Developers would have wasted their energy and time in debugging what was wrong with the code and why the expected behavior wasn't the customized functionality. These kind of errors can be found at the compile-time rather than at the run-time, if the annotation @Override is applied on the re-defined method in the sub-class. If the method is annotated with @Override and if there is an mismatch in the method name or in the argument list, then the compiler will immediately report an error message like, My customized functionality

"The method getTableCellEditorComponet of type 'MyCustomizedTableCellEditor' must override a super-class method". 3.2) The @SuppressWarnings Annotation With the concept of Generics starting from Java 5.0, references to any of the Collection types should be parameterized. Else the compiler will issue a warning message. For example, consider the following legacy code which makes use of a raw List type, LegacyCode.java package builtin.suppress; import java.util.*; public class LegacyCode { public static List toList(Object[] array) { return Arrays.asList(array); } } If the code gets compiled by using a Java 5 Compiler, then we would probably see a message something very similar to this. "List is a raw type. References to generic type List<E> should be parameterized". The compiler suggests not to use raw type for Collection without being parameterized with some type. For getting rid of this warning message, one can define the return type like List<Object> thereby making the compiler happy. Assume the case of legacy projects when its get

compiled by a Java 5.0 compiler. We can see numerous of warning like this. It is highly impossible to go and fix everywhere. Consider the case where a project is depending on a third-party library, in which case the dependant source code cannot be changed. The solution for this problem is to make use of @SuppressWarnings Annotation. The following shows the definition of a Suppress Warning Annotation, SuppressWarnings.java @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); } Note that Suppress Warnings can be used on various Java elements like Class/Interface, Method, Constructor etc. When applied on any of the Java Elements along with a value, it provides a hint to the Compiler to turn-off the warnings. Consider the following code, @SuppressWarnings("unchecked") public static List toList(Object[] array) { return Arrays.asList(array); } The above code provides a hint to the compiler to suppress the unchecked warnings. Following is another example of Suppress Warnings. We know that it is a good practice to provide a Serial

Version Identifier for a class that implements the Serializable interface. Consider the following code, LegacyCode.java

package builtin.suppress; import java.io.Serializable; import java.util.*; public class LegacyCode implements Serializable { @SuppressWarnings("unchecked") public static List toList(Object[] array) { return Arrays.asList(array); } } The compiler will warn saying that for a Serializable class, there should be a definition of Serial Version Identifier like this :"The serializable class LegacyCode does not declare a static final serialVersionUID field of type long". This can be suppressed (although its not a good practice) by using the following Annotation. LegacyCode.java @SuppressWarnings("serial") public class LegacyCode Serializable

implements

{ } Note that in the above case the Annotation is applied in the class-level. 3.3) The @Deprecated Annotation The Interfaces and the Classes in an Application get updated every now and then. Interfaces used by the Clients may undergo so many revisions in the form of new methods being added and existing methods being removed or updated. Imagine the case where a method defined in a class or an interface has now become obsolete and we should warn the Client Applications not to make use of them. One dangerous way is to remove the method itself from the Interface. But this solution has a huge impact on the code that is dependant on this interface. Another elegant way is to mark the old method as deprecated which informs the client not to use this method anymore because in the future versions this old method may not be supported. Clients may prepare themselves not to depend on the old method anymore. For example, consider the following code, MyOldClass.java package builtin.deprecated; public class MyOldClass { /** * This method has Client Applications now can * depend on

become

obsolete.

* {@link #myAlternativeMethod()} achieve the same affect. */ @Deprecated public void myDeprecatedMethod() { // Obsolete code here. } public void myAlternativeMethod() { // Revised code here. } }

to

The class MyOldClass has a method myDeprecatedMethod() which is tagged as Deprecated. Now if any of the Client code tries to access this method, then the following warning message is issued by the compiler. "The method myDeprecatedMethod() from the type MyOldClass is deprecated". It is always nice to mention the alternate ways in the JavaDoc itself. For example, the JavaDoc for myDeprecatedMethod() method suggests to use the method myAlternativeMethod() as an alternative.

) User-defined Annotations In this section, we shall see how to annotate objects that we may come across in day-to-day life. Imagine that we want to persistent object information to a file. An Annotation called Persistable can be used for this purpose. An important thing is that we want to mention the file in which the information will get stored. We can have a property called fileName within the declaration of Annotation itself. The definition of the Persistable Annotation is given below, Persistable.java

@Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE}) public @interface Persistable { String fileName(); } Note that we have specified the target for this Annotation as Field and Local variable meaning that this Annotation can be applicable only to Java Fields and Local variables. Also note how the property fileName for this Annotation is defined. The type (String, in this case) followed by the property name which looks like a method definition fashion. Having defined this Annotation, let us see how to apply this Annotation on Java Elements. Consider the following code snippet, @Persistable(fileName = "movies.txt") Movie movie; The above code tells to save all the information about the Movie object to the file called movies.txt. But who will do this operation? We haven't defined any code that will do this operation. Generally Annotations are interpreted by Tools and once Tools read these Annotation informations, they will perform the required operation. If we want to give default values to a property, then we should follow the strange syntax. Persistable.java @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE}) public @interface Persistable

{ String "defaultMovies.txt"; } fileName() default

In this case, if the clients didn't supply a value to the fileName property, then the default value defaultMovies.txt is taken. Now let us see the syntax of specifying multiple values to a property. Let us assume that we want to save the status of the object in multiple files in which case we should have declared an array to hold the filenames. Persistable.java @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE}) public @interface Persistable { String[] fileNames(); } And now Clients can given multiple file entries using the traditional array syntax like this, @Persistable( fileNames = { "movie1.txt", "movie3.txt"} ) Movie movie; 5) Reflecting the Annotation Information 5.1) Introduction

"movie2.txt",

In this section, let us see how to query for information related to Annotation types using Reflection. As we all know, Java Reflection API is used to dynamically discover information (meta-data information too) for any type of Java Element like Class, Interface, Field, Method, Constructor etc. From Java 5.0 onwards, the Reflection API has been extended to add support for Annotation types too. A new interface called Annotated Element (AnnotationElement) has been added to the java.lang.annotation package. This interface is primarily used to tell that the element in consideration is an Annotation Element which means that all the existing classes/interfaces like java.lang.Package, java.lang.Class, java.lang.reflect.Constructor, java.lang.reflect.Field and java.lang.reflect.Method implement the AnnotationElement interface. Following are the useful methods available in the AnnotationElement interface,
y

Boolean isAnnotationPresent(Class) This method will return true if the annotation type (which is passed as an argument) is present in the calling element object. Annotation getAnnotation(Class) When given an Annotation class on the calling element, gives the corresponding Annotation object. Annotation[] getAnnotations() Returns an array of Annotations that are present on the calling object.

5.2) Example Program Let us look into a simple example program that makes use of the new interfaces and the methods to query for Annotation information at runtime. For this example, let us define two Annotation types as follows. One is the Author Annotation and the other is the Version Annotation.

This is a very common piece of meta-information that can be given to any Java file (or any other type of file). Author.java package reflections; import java.lang.annotation.*; @Target(value = {ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Author { String name() default "unknown"; } Note the definition of the Author annotation. This is a single-valued Annotation meaning that this Annotation has a single property called name. Also make a note of the Target Annotation. The presence of Target Annotation tells that Author Annotation can only be applied to Java elements like Constructor, Method and Type (Class/Interface). Another important thing to note is the Retention for this Annotation is set to Run-time because we want this Annotation information to be available to the JVM while the program is running. The name property is also given a default value unknown if the consuming Application fail to provide an explicit value. Following is the definition of the Version Annotation. It looks exactly the same as Author Annotation except the fact that it has a property called number (which is of type double) to hold the version value. ersion.java

package reflections; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = {ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Version { double number(); } Let us have a look at the following class definition that makes use of the above declared Annotations. The Annotations are applied at the classlevel as well as in the method-level. AnnotatedClass.java package reflections; @Author(name = "Johny") @Version(number = 1.0) public class AnnotatedClass { @Author(name = "Author1") @Version(number = 2.0f) public void annotatedMethod1() { }

@Author(name = "Author2") @Version(number = 4.0) public void annotatedMethod2() { } } This program makes use of the new API to read the Annotation related information that are applied on various Java Elements. The utility method readAnnotation() takes a parameter of type AnnotationElement which can represent a Class, method or Constructor. Then it queries for a list of Annotations of that particular element by calling the getAnnotations() method. Then the array is iterated to get the individual element, then made a downcast to extract the exact information - Author.name() and Version.number(). AnnotationReader.java package reflections; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; public class AnnotationReader { public static void main(String args[]) throws Exception { Class<AnnotatedClass> classObject = AnnotatedClass.class; readAnnotation(classObject);

Method method1 classObject.getMethod("annotatedMethod1", Class[]{}); readAnnotation(method1); Method method2 classObject.getMethod("annotatedMethod2", Class[]{}); readAnnotation(method2); }

= new = new

static void readAnnotation(AnnotatedElement element) { try { System.out.println("\nFinding annotations on " + element.getClass().getName()); Annotation[] classAnnotations = element.getAnnotations(); for(Annotation classAnnotations) { if instanceof Author) { Author (Author)annotation; System.out.println("Author author.name()); } else if instanceof Version) name:" + author = (annotation annotation :

(annotation

{ Version (Version)annotation; System.out.println("Version number:" + version.number()); } } } catch (Exception exception) { exception.printStackTrace(); } } } The output for the above program is as follows, Output Finding annotations on java.lang.Class Version number:1.0 Author name:Johny Finding annotations java.lang.reflect.Method Author name:Author1 Version number:2.0 Finding annotations java.lang.reflect.Method Author name:Author2 Version number:4.0 on version =

on

6) Conclusion Before the advent of Annotations, Applications had to define their metadata in some configuration files and usually these meta-data were externalized from the Application. Now, they can directly define this meta-data information in the source code. The concept of Annotating elements in Java is gaining much popurality because of its robustness and simplicity of usage. When effectively applied on Java elements, Annotations can be of great use.

You might also like