Annotations — Since Java 5
New in Java 5, annotations (also called metadata) allow you to add information to your code that the compiler or some other tool can use. An annotation can be used anywhere a Java modifier (e.g., public) can be used. (Style: annotations first.)
Think of an annotation as a Post-It® note you put on some piece of code.
For example the annotation @Deprecated can be used as a method modifier to inform the compiler the method, although legal, is discouraged in favor of a new (unspecified) method. @Override can be used as a method modifier when overriding inherited methods. This annotation tells the compiler you intend the method to override something and not overload or define a new method. The compiler generates an error if you make a mistake like this:
class
Foo {
@Override public boolean equals( Foo f) { ... }
}
should produce an error, but wouldn’t without the annotation.
JUnit 4 shows a good use of annotations. It is in many ways easier to use than JUnit 3. All you do to define a test method is precede it with @Test annotation. That’s it! When you later run the JUnit program, giving it the class containing your tests, it finds the test methods and runs them using this annotation. TestNG uses this too.
Code Generation and Interjection
Other types of annotations can be defined (by anyone) and used to automatically generate “boilerplate” code. For example @Property might be used with the new apt tool to mark some properties of a class as JavaBean properties, and apt can automatically generate the get and set methods required. Or an @RMI annotation could be defined that would cause stub and skeleton classes to be generated automatically. In JavaEE, EJBs use these to automatically manage objects and to initialize instance properties. This is called interjection.
Standard Java 5 SE Annotations
Java 5 defines these annotations as standard (see java.lang (scroll to bottom) and java.lang.annotation package): @Deprecated, @Documented, @Inherited, @Override, @Retention, @SuppressWarnings, and @Target.
A programmer should know and use @Deprecated, @Override, and @SuppressWarnings; the rest are only useful when defining your own annotations.
Annotations other than these standard ones won’t do anything on their own; you’ll need to pre-process your source code with apt, javadoc, or some other tool that recognizes those annotations. Before annotations, you marked methods deprecated using a special Java doc method (the compiler peeks at these): /** @deprecated ...reason...*/. You should still do this, to document why the method is deprecated and what should be used instead.
Note! It is not an error to use a deprecated method or variable from within that same class!
Annotations can optionally be passed values called elements. These values are then available to the extra tools that pay attention to them. For example say what warning messages to suppress with: @SuppressWarnings(value="unchecked"). Here value is an element of the annotation, which may define as many of these as desired. Each element can be of a simple type or an array.
As a convenience, when there is only one element defined and it has the name value, you can omit the name. So the above can be simplified to: @SuppressWarnings("unchecked"). Also, if there are no elements at all you can omit the parenthesis too.
With @SuppressWarnings, the list of warning that can be suppressed varies depending on the compiler used. There is a list for Eclipse in the on-line docs (search for suppresswarnings). Netbeans uses (apparently) the JDK ones. For javac version 1.6 and earlier you can see a list by “javac -X” and look at the list that shows for the “Xlint” option. However, not all of these work as of 1.6.0_18 (especially “all”)! (See the list on-line at knol.google.com/k/suppresswarnings-annotation-in-java.)
Using Annotations
Using annotations is simple; it is defining them that gets tricky. Just place one or more annotations directly before the thing you are annotating. If there are no elements passed to the annotation, you don’t need any parenthesis. So both of these work the same way:
@MarkerAnnotation
class Foo { ... }
@MarkerAnnotation() class Foo { ... }
(Annotations that take no element arguments are called marker annotations.)
If an annotation takes exactly one value (for the element named value), you can just pass the value (of the correct type). If there is more than one element value being passed, you must use the syntax name=value, ... For example, an annotation that takes one String element could be used either way:
@OneItemAnnotation("XYZ") class Foo { ... }
@OneItemAnnotation(value="XYZ") class Foo { ... }
If the value is an array, you can use the literal array notation like this:
@Reviewers( {"Moe", "Larry", "Curly"} ) class Foo {...}
(Show example on Website.)
Defining Your Own Annotations
To define your own annotation you create a special type of interface. An annotation type with no elements is termed a marker annotation type. Each element of an annotation (really its properties) are defined as method declarations. Element declarations must not have any parameters, generics, or throws clauses; they’re not really methods at all.
Element types are restricted to primitives plus String, Class, enum, annotation, and arrays of those types.
@interface AnnotationName
{} // A marker annotation
// with no content
In annotations with a single element, the element should be named value:
@interface AnnotationName
{
elementType value();
}
@interface AnnotationName
{
elementType elementName1();
elementtype elementName2(); // ...
}
You can specify default values for elements too:
@interface AnnotationName
{
elementType elementName() default literal;
...
}
You can specify if Java doc comments should show the annotation or not:
@Documented
public @interface RFE {
int id();
String description();
String assignedTo() default "[unassigned]";
String dateRequested() default "[unknown]";
}
Note, only things that show up in the javadoc HTML output can show annotations. That is, the annotations on local variables or private stuff won’t appear.
Here’a a use of the RFE annotation:
@RFE( id = 123, description
= "Enable telepathy",
assignedTo = "Hymie Piffl", date = "2/30/2010" )
public static void readMinds () { ... }
When defining your own annotations you can add annotations to it:
· Some annotations are for internal use only. Other annotations are part of the public interface of the class (or whatever) and user’s of your code need to know about them. To cause javadoc (or other similar tools) to include an annotation in the generated documentation, annotate your annotation with @Documented.
· You use @Target to restrict where your annotation can be applied. The default is anything can be annotated, but you can restrict to local variables, fields, methods, classes, or packages (see java.lang.annotation.ElementType). For example, to restrict some annotation to be used only on methods, use:
@Target(
ElementType.METHOD )
@interface MyMethodAnnotation { ... }
· @Inherited shows that the annotation may be inherited (i.e., mark the parent with the annotation, and the child will show as having it too).
· You can control if your annotation will be part of the Class object loaded into RAM (RUNTIME), in the .class file but not the Class object (CLASS), or just in the source code and not in the .class file (SOURCE). This is known as the retention policy. for example:
@Retention( RetentionPolicy.SOURCE ) // ibid
To use reflection to check for annotations, the policy must be RUNTIME.