Annotations
Overview of
Annotations
Annotations are notes for the
Java compiler. When you annotate a program element in a source file, you add
notes to the Java program elements in that source file. You can annotate Java
packages, types (classes, interfaces, enumerated types), constructors, methods,
fields, parameters, and local variables. For example, you can annotate a Java
class so that any warnings that the javac program would otherwise issue will be
suppressed. Or, you can annotate a method that you want to override to ask the
compiler to verify that you are really overriding the method, not overloading
it. The Java compiler can be instructed to interpret annotations and discard
them (so those annotations only live in source files) or include them in
resulting Java classes. Those that are included in Java classes may be ignored
by the Java virtual machine, or they may be loaded into the virtual machine.
The latter type is called runtime-visible and you can use reflection
to inquire about them.
Annotations and Annotation Types
When studying annotations, you
will come across these two terms very frequently: annotations and annotation
types. To understand their meanings, it is useful to first bear in mind that an
annotation type is a special interface type. An annotation is an instance of an
annotation type. Just like an interface, an annotation type has a name and
members. The information contained in an annotation takes the form of key/value
pairs. There can be zero or multiple pairs and each key has a specific type. It
can be a String, int, or other Java types. Annotation types with no key/value
pairs are called marker annotation types. Those with one key/value pair are
often referred to single-value annotation types. Annotations were first added
to Java 5, which brought with it, three annotation types: Deprecated Override,
and Suppress Warnings. They for example, here is how you use the marker
annotation type Deprecated:
@Deprecated
And, this is how you use the
second syntax for multi-value annotation type
Author:
@Author(firstName="Ted",lastName="Diong")
There is an exception to this
rule. If an annotation type has a single
key/value pair and the name of the key is value, then you can omit the
key from the bracket. Therefore, if fictitious annotation type Stage has a
single key named value, you can write
@Stage(value=1) or @Stage(1)
Returns a string representation
of this annotation, which typically lists all the key/value pairs of this
annotation.
Standard Annotations:
Annotations were a new feature in
Java 5 and originally there were three standard annotations, all of which are
in the java.lang package: Override, Deprecated, and Suppress Warnings.
Override
Override is a marker
annotation type that can be applied to a method to indicate to the
compiler that the method overrides a method in a superclass. and Child classes.
However, you won’t be always this lucky. Sometimes the parent class is buried
somewhere in another package. This seemingly trivial error could be fatal
because when a client class calls the calculate method on a Child object and
passes two floats, the method in the Parent class will be invoked and the wrong
result returned. Using the Override annotation type will prevent this kind of
mistake. Whenever you want to override a method, declare the Override
annotation type before the method:
Ex:-
public class Child extends Parent
{
@Override
public int calculate(int a, int b)
{
return (a + 1) * b;
}
}
This time, the compiler will
generate a compile error and you’ll be notified that the calculate method in
Child is not overriding the method in the parent class. It is clear that
@Override is useful to make sure programmers override a method when they intend
to override it, and not overload it.
Deprecated
Deprecated is a marker annotation
type that can be applied to a method or a type to indicate that the method or
type is deprecated. A deprecated method or type is marked so by the programmer
to warn the users of his code that they should not use or override the method or
use or extend the type. The reason why a method or a type is marked deprecated
is usually because there is a better method or type and the deprecated method
or type is retained in the current software version for backward compatibility.
For example, the DeprecatedTest class in Ex.1 uses the Deprecated annotation
type.
Ex.1: Deprecating a method
package app1;
public class DeprecatedTest
{
@Deprecated
public void serve()
{ }
}
If you use or override a
deprecated method, you will get a warning at compile time. For example, EX.2
shows a DeprecatedTest2 class that uses the serve method in DeprecatedTest.
Ex.2: Using a deprecated method
package app1;
public class DeprecatedTest2
{
public static void main(String[] args)
{
DeprecatedTest test = new
DeprecatedTest();
test.serve();
}
}
Compiling DeprecatedTest2
generates this warning: Note: app1/DeprecatedTest2.java uses or overrides a
deprecated
Suppress Warnings:
Suppress Warnings is used, as you
must have guessed, to suppress compiler warnings. You can apply
@SuppressWarnings to types, constructors, methods, fields, parameters, and
local variables. You use it by passing a String array that contains warnings
that need to be suppressed. Its syntax is as follows.
@SuppressWarnings(value={string-1,
..., string-n})
Where string-1 to string-n
indicate the set of warnings to be suppressed. Duplicate and unrecognized
warnings will be ignored.The following are valid parameters to
@SuppressWarnings:
▪unchecked. Gives more detail for
unchecked conversion warnings that are mandated by the Java Language
Specification.
▪path. Warns about nonexistent
path (classpath, sourcepath, etc) directories.
As an example, the
SuppressWarningsTest class in Ex:-.4 uses the SuppressWarnings annotation type
to prevent the compiler from issuing unchecked and fallthrough warnings.
Ex 4 Using @SuppressWarnings
package app1;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
@SuppressWarnings(value={"unchecked","serial"})
public class SuppressWarningsTest
implements Serializable
{
public void openFile()
{
ArrayList a = new ArrayList();
File file = new
File("X:/java/doc.txt");
}
}
Common Annotations:
▪ comments. Comments accompanying the generated code. For example, in Ex.5
@Generated is used to annotate a generated class.
Ex.5: Using @Generated
package app18;
import
javax.annotation.Generated;
@Generated(value="com.chandrasoftware.robot.CodeGenerator",
date="2012-11-31",
comments="Generated code")
public class GeneratedTest
{
}
Standard Meta-Annotations:
types.
Documented
Documented is a marker annotation
type used to annotate the declaration of an annotation type so that instances
of the annotation type will be included in the documentation generated using
Javadoc or similar tools.For example, the Override annotation type is not
annotated using Documented. As a result, if you use Javadoc to generate a class
whose
method is annotated @Override,
you will not see any trace of @Override in the resulting document. For
instance, Ex.6 shows a OverrideTest2 class that uses @Override to annotate the
toString method.
Ex.6: The OverrideTest2 class
package app1;
public class OverrideTest2
{
@Override
public String toString()
{
return "OverrideTest2";
}
}
On the other hand, the Deprecated
annotation type is annotated @Documented. Recall that the serve method in the
DeprecatedTest class in Ex.2 is annotated @Deprecated. Now, if you use Javadoc
to generate the documentation for OverrideTest2 the details of the serve method
in the documentation will also include @Deprecated, like this: serve
@Deprecated
public void serve()
▪SOURCE. Annotations are to be
discarded by the Java compiler.
▪CLASS. Annotations are to be
recorded in the class file but not retained by the JVM. This is the default value.
▪RUNTIME. Annotations are to be
retained by the JVM so they can be queried using reflection. For example, the
declaration of the SuppressWarnings annotation type is annotated @Retention
with the value of SOURCE.
@Retention(value=SOURCE)
public @interface
SuppressWarnings
Target:
Target indicates which program
element(s) can be annotated using instances of the annotated annotation type.
The value of Target is one of the members of the
java.lang.annotation.ElementType enum: As an example, the Override annotation
type declaration is annotated the following Target annotation, making Override
only applicable to method declarations.
@Target(value=METHOD)
You can have multiple values in
the Target annotation. For example, this is from the declaration of
SuppressWarnings:
@Target(value={TYPE,FIELD,
METHOD, PARAMETER,CONSTRUCTOR,
LOCAL_VARIABLE})
Custom Annotation Types:
An annotation type is a Java
interface, except that you must add an at sign before the interface keyword
when declaring it.
public @interface
CustomAnnotation
{
}
Using Custom Annotation Types:
The Author annotation type is
like any other Java type. Once you import it into a class or an interface, you
can use it simply by writing
@Author(firstName="firstName",lastName="lastName",
internalEmployee=true|false)
For example, the Test1 class in Ex.8
is annotated Author.
Ex.8: A class annotated Author
package app1.custom;
@Author(firstName="John",lastName="Guddell",internalEmployee=true)
public class Test1 {
}
The next subsection “Using
Reflection to Query Annotations” shows how the Author annotations can be of
good use. Using Reflection to Query Annotations The java.lang.Class class has
several methods related to annotations.
public A getAnnotation (Class
annotationClass) Returns this element’s annotation for the specified annotation
type, if
present.
Otherwise, returns null. public
java.lang.annotation.Annotation[] getAnnotations()
Returns all annotations present
on this class.
public boolean isAnnotation() Returns
true if this class is an annotation type.
public boolean
isAnnotationPresent(Class
java.lang.annotation.Annotation>
annotationClass)
Indicates whether an annotation
for the specified type is present on this class The app1.custom package
includes three test classes, Test1, Test2, and Test3, that are annotated
Author. Ex.9 shows a test class that employs reflection to query the test
classes.
Ex.9: Using reflection to query
annotations
package app1.custom;
public class CustomAnnotationTest
{
public static void printClassInfo(Class c)
{
System.out.print(c.getName() + ".
");
Author author = (Author)
c.getAnnotation(Author.class);
if (author != null)
{
System.out.println("Author:"
+ author.firstName()
+ " " +
author.lastName());
} else {
System.out.println("Author
unknown");
}
}
public static void main(String[] args)
{
CustomAnnotationTest.printClassInfo(Test1.class);
}
}