Sei sulla pagina 1di 7

Dr.

Dobb's | Java Annotations and apt | July 01, 2005

1 of 7

http://drdobbs.com/article/print?articleId=184406144&siteSectionName=jvm

Source Code Accompanies This Article. Download It Now.

janotate.txt
janotate.zip

Java 5.0 introduces "annotations" that integrate metadata technology directly into the language.
By J. Benton
July 01, 2005
URL:http://drdobbs.com/jvm/184406144

J. Benton is a professional Java programmer and computer science graduate student with research interest in
AI planning. He can be reached at j.benton@acm.org.
Java developers have long been able to add metadata to code using comment-eating tools such as Javadoc and
XDoclet. However, Java 5.0 introduces a language facility called "annotations" that integrates a metadata
technology directly into the language, providing a standard for giving attributes to declarations (see JSR-175,
(http://www.jcp.org/en/jsr/detail?id=175). To support annotations, Sun Microsystems released apt, an
"Annotation Processing Tool" that lets you write your own annotation processors (http://java.sun.com/j2se/1.5.0
/docs/guide/apt/GettingStarted.html). This makes it possible for you to build programs that act upon annotations
for analyzing Java source files and automatically generating boilerplate code. It does this by providing the mirror
API, which provides functionality similar to the reflection API built into the Standard Java libraries. In this
article, I examine annotations and show how you use apt to analyze Java source files and generate boilerplate
code.
You use annotations just as you would any modifying keyword (final, static, transient, and the like), except you
put "@" in front of it. Each annotation has a set of intended declaration targets. That is, an annotation is intended
to be placed before a package, type (including class, enum, and interface), field (including an enum constant),
constructor, method, parameter, local variable declaration, or any combination of these. Three types of
annotations exist within Java, each with slightly different syntax. Table 1 describes each type.

java.lang Annotations
Syntax always gets clearer as you see examples of real usage. Happily, J2SE 5.0 supplies annotations built into
the API to explore. Three are intended to be used by Java compilers and defined in java.lang, so no import is

2/18/2012 5:37 PM

Dr. Dobb's | Java Annotations and apt | July 01, 2005

2 of 7

http://drdobbs.com/article/print?articleId=184406144&siteSectionName=jvm

required to use them.


@Deprecated. As a standard practice with Java, any declaration that has become obsolete or should not
be used for some reason, whether replaced or not, becomes deprecated. Before this annotation, the only
way to mark a deprecated declaration was to use Javadoc. This annotation helps compilers and IDEs tell
you when you're using a deprecated declaration. It's a marker annotation (no values required). You can use
this to annotate any item; see Example 1.
@SuppressWarnings. Compilers warn you when your code is syntactically legal but potentially
problematic. Sun's javac compiler gives you the ability to turn on warnings using the -Xlint option, which is
very much connected with the @SuppressWarnings annotation. However, each compiler can define its
own warnings (although there's a request from Sun that compiler writers work to make the warning values
compatible).
To compile Example 2, you'd enter:
javac -Xlint:unchecked
UncheckedExample.java
This returns a warning. When you add @SuppressWarnings(value={"unchecked"}) as in
Example 3, you get no warning.
You can suppress more than one warning by putting more into the value array; for instance,
@SuppressWarnings(value={"unchecked", "finally"}). Table 2 lists the possible options that
can be given to -Xlint (given to javac in the form of -Xlint:<option>). If you give no options
to -Xlint, it enables all warnings. You can also disable warnings through -Xlint by doing
-Xlint:-<option>. Again, including the extra minus disables a warning.
@Override. If you think you're overriding a method, use the @Override annotation, which
lets the compiler catch the common problem of thinking you're overriding something when
you aren't. This is especially useful to keep track of class hierarchy. For instance, if I try to
implement java.util.Collection, then try to override the method Object get(int index), I get a
compiler error. If I neglected to use the annotation, the compiler would just think I'm creating
a new method. Example 4 fails to compile because the List interfacenot the Collection
interfacedefines get(). List extends Collection, which is occasionally a source of confusion
for those who are used to exclusively using Lists.

Meta Annotations
An annotation type defines annotations, just as an interface defines an object. In fact, annotation types exist as
interfaces and act like them in some respects. They automatically extend the java.lang.annotation.Annotation
interface and have methods with return types. Their methods can only return primitive types, strings, enums,
another annotations, and arrays of those things.
In addition to the three previous annotation classes in the java.lang package, four are defined in
java.lang.annotation. All the annotations in that package are meta annotationsannotations that annotate
annotation types.
@Retention. Annotations can have one of three retention policies in Java 5.0. Annotations having
"source" retention (RetentionPolicy.SOURCE) exist only in source code and don't get compiled into
bytecode. Annotations with "class" retention (RetentionPolicy.CLASS) are intended to be read at the
bytecode level, after compilation and before runtime. Finally, runtime retention
(RetentionPolicy.RUNTIME) indicates that an annotation can be accessed through reflection at runtime. I
focus on annotations with "source" retention because apt is a source-level processing tool.
@Target. I previously described the type of targets. You can specify a single target using a single value or

2/18/2012 5:37 PM

Dr. Dobb's | Java Annotations and apt | July 01, 2005

3 of 7

http://drdobbs.com/article/print?articleId=184406144&siteSectionName=jvm

multiple ones using an array. Also, giving an empty array to an annotation of this type holds special value.
It forces you to use annotation types only as a return type within other annotation types. The apt examples
show why this is sometimes necessary when you want multiple annotations of the same type to be given
on a single item.
@Documented. The @Documented meta annotation is intended for use by document-generation tools like
javadoc. Javadoc generates documentation for an annotation type if, and only if, this meta annotation is
applied to it.
@Inherited. As meta annotations go, @Inherited probably ranks as the most interesting (although it is also
excluded from the apt examples for simplicity). It lets annotations be inherited by subclasses of the
annotated class. If an annotation type uses this meta annotation, any type (including classes, interfaces,
and enums) that it annotates lets this annotation be inherited to its subtypes. This is an "if and only if"
conditionif you don't want subtypes to inherit this annotation, then don't use it. (Annotations are
inherited on the annotation types themselves. Overridden methods will not inherit annotations.)

Boilerplate Code and apt


In their short history, Java annotations have become widely known to be useful for generating boilerplate code. I
present an example of this by showing an annotation processor using apt. The apt annotation processor generates
new exception classes based upon an annotation given to a type. The exception is slightly different depending
upon the values given to the annotation. Listing One contains one of two annotation types required for this
example.
Annotations give the "default" keyword a second meaning (the first being the "default case" in the switch
statement). You use it in annotation type definitions to specify the default value that a method in an annotation
returns if the value is not specified.
In Listing One, @Target contains an empty array. The Java compiler won't let you use the same annotation type
more than once on the same item. To get around this, you have to create an annotation that takes an array of
ApplicationException annotations as its value. The only way you can use an annotation without a target is within
another annotation, so defining an annotation in this way ensures proper semantics for the annotation processor.
Listing Two shows ApplicationExceptions, the annotation type containing an array of ApplicationException.
ApplicationExceptions targets types.
The real power of apt comes from the way it processes annotations in Java source files. The tool uses
annotations to:
Zero in on the files it needs to process.
Analyze the files appropriately and produce boilerplate code or external configuration files.
When apt generates a source file, it processes and compiles it in turn. ApplicationExceptionsApf.java (available
electronically; see "Resource Center," page 3) shows the annotation processor factory and class for creating the
boilerplate code based upon the ApplicationExceptions annotation. Notice the structure of the factory class:It
extends com.sun.mirror.apt.AnnotationProcessorFactory and overrides "AnnotationProcessor
getProcessorFor(Set, AnnotationProcessorEnvironment)". You give a list of annotations that the factory in
ApplicationExceptionsApf.java has processors to handle with the variable supportedAnnotations. The provided
list can sometimes look like Java's import facility, but don't be fooledit acts differently. Unlike package
imports, com.ddj.annotations.* means all annotations defined in the package com.ddj.annotations and all of its
subpackages. Also, you can specify "*" to mean all annotations can be supported by this factory (this has sort of
a double meaning, as it denotes this annotation processor can process any fileeven ones containing no
annotations). If you want to define more than one package, include them in the array.
You can also define your own, user-defined options for annotation processors. To do this, you let the method
supportedOptions() return a collection of options beginning with -A. For instance, say you want the processor to

2/18/2012 5:37 PM

Dr. Dobb's | Java Annotations and apt | July 01, 2005

4 of 7

http://drdobbs.com/article/print?articleId=184406144&siteSectionName=jvm

have the ability to override the additional information given in the example, so you add -AIgnoreAddedInfo as a
supported option. The list only adds the convenience of checking the "supported options" against the options
provided to apt. All of apt's own options and those starting with an -A, whether supported or not, get passed to
the processor through environment.
In the example, the method getFactoryFor() works in a straightforward manner. It checks that the given
supported options match those given to apt. After that, it goes through the annotations that apt has found that the
annotation processor also claims to support (given through the atds set). Once it finds
com.ddj.annotations.ApplicationExceptions, it returns the proper annotation processor. If it fails to find the
suitable annotation, it returns a NO_OP annotation. Use NO_OP whenever you want to return an annotation
processor that does nothing.
Also in ApplicationExceptionsApf.java is the annotation processor itself. It goes into the processor factory class
as a static inner class. If you're familiar with Java reflection, you recognize the methods used to analyze the
code. Sun's mirror API works much like reflection, except it refers to source code rather than loaded classes and,
therefore, works on types and declarations. Table 3 shows each package in the mirror API and a short
description of each.
The processor is simple. It first looks at each type declaration given to the apt environment. It then gets the
ApplicationExceptions annotation and processes each ApplicationException value within, generating a .java file
containing the appropriate exception definition.
Next, you'll need to use this annotation processor. apt provides two ways of doing this: You can either use apt's
built-in annotation processor factory discovery facility, or specify the processor on a command line. You'd use
the discovery procedure when you have more than one annotation processor factory. Because only one
annotation processor factory exists here, I stick to the command line. You can use the discovery procedure by
putting a file called "META-INF/services/com.sun.mirror.apt.AnnotationProcessorFactory" in a .jar file that lists
each annotation processor factory that you created. The .jar file also needs to contain all factories listed and their
processors. You then can put the .jar file in your class path and apt tries to choose the proper factory for each
.java file you're processing, based upon supported annotations you list within each factory.
Listing Three is an example of using the ApplicationExceptions class. Again, I use a command-line option to
generate and execute the annotation processor. Before executing the following command, make sure the tools.jar
file is in your classpath and that you've compiled the annotation processor factory:
apt -factory com.ddj.apt.processors.
ApplicationExceptionsApf com\ddj\*.java
This should have generated two files: TestException.java contains an extra integer and AppException.java exists
as a plain extension to Exception.

Conclusion
In this article, I introduced the annotations built into J2SE 5.0 and looked at how you can use apt to generate
boilerplate exception code. I encourage you to expand upon the code. It's important to note, however, that apt's
days may be numbered. The recently approved JSR-269, the Pluggable Annotation Processing API, expressly
states that it is intended to supersede Sun's mirror API and apt. If it stays on schedule, it will likely ship with Java
6.0 (code-named "Mustang").
DDJ

2/18/2012 5:37 PM

Dr. Dobb's | Java Annotations and apt | July 01, 2005

5 of 7

http://drdobbs.com/article/print?articleId=184406144&siteSectionName=jvm

Listing One
/* ApplicationException.java * Created on Jan 2, 2005
package com.ddj.annotations;

*/

import java.lang.annotation.*;
/** This annotation is used by the annotation processing tool (apt)
* to generate an exception. * @author J. Benton */
@Retention(RetentionPolicy.SOURCE)
// I give an empty array to "@Target" to say that this annotation
// type should be used only as an element of other annotations
// (you cannot use this to annotate anything directly).
@Target({})
public @interface ApplicationException {
/** What I want to call this exception (sans the word "Exception".*/
String exceptionName();
/** I must add extra information to this exception to make it useful. */
String addedInformationType() default "";
/** I should call the "extra information" something that is recongizable if
* you try to read the generated exception class. */
String addedInformationVariableName() default "";
}

Back to article

Listing Two
/* ApplicationExceptions.java * Created on Jan 2, 2005 */
package com.ddj.annotations;
import java.lang.annotation.*;
/** This was made so I can generate a set of exceptions for an application
* on the main application class. * @author J. Benton */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface ApplicationExceptions {
/** An array of ApplicationExceptions. I do things this way because we can't
* have more than one instance of the same annotation type on any item. */
ApplicationException[] applicationExceptions();
}

Back to article

Listing Three
/* ExceptionAnnotationTest.java * Created on Jan 3, 2005 */
package com.ddj;
import com.ddj.annotations.*;
/** Use this to test the annotations. * @author J. Benton */
@ApplicationExceptions(
applicationExceptions={@ApplicationException(exceptionName="Test",
addedInformationType="int",
addedInformationVariableName="status"),
@ApplicationException(exceptionName="App")})
public class ExceptionAnnotationTest {
}

Back to article

2/18/2012 5:37 PM

Dr. Dobb's | Java Annotations and apt | July 01, 2005

6 of 7

http://drdobbs.com/article/print?articleId=184406144&siteSectionName=jvm

/** Deprecating a method using the annotation. @author J. Benton */


public class DeprecateExample {
// if you use this method, you'll get a warning that it is deprecated.
@Deprecated
public void ohNoIAmDeprecated() {
System.out.println("I am deprecated!");
}
}
Example 1: Deprecating a method using annotation.
import java.util.ArrayList;
/** Suppressing warnings. @author J. Benton */
public class UncheckedExample {
public void uncheckedGenerics() {
ArrayList blahblah = new ArrayList();
// warning, warning, warning!! unchecked call below!
blahblah.add(2);
}
}
Example 2: Generating a warning.
import java.util.ArrayList;
/** Suppressing warnings. @author J. Benton */
// You can use so-called single-valued annotations
// just like regular annotations if you want (note
// how I am specifying the method name here, though
// it is unnecessary).
@SuppressWarnings(value={"unchecked"})
public class UncheckedExample {
public void uncheckedGenerics() {
ArrayList blahblah = new ArrayList();
// no warning! I suppressed it!
blahblah.add(2);
}
}
Example 3: Using the @SuppressWarnings annotation.
package com.ddj;
import java.util.*;
/** Using the @Override annotation. @author J. Benton */
public class OverrideExample implements Collection {
// ...
/** This won't work! I'm not overriding anything here!
* This is a new method--not one being overridden.
* @param index The index of the thing to get.
* @see java.util.List#get(int)
*/
@Override
public Object get(int index) {
Object value = null;
// code to get stuff.
return value;
}
// ...
}
Example 4: Using the @Override annotation.

2/18/2012 5:37 PM

Dr. Dobb's | Java Annotations and apt | July 01, 2005

7 of 7

http://drdobbs.com/article/print?articleId=184406144&siteSectionName=jvm

Annotation

Description

Example Syntax

Marker

Takes no values

@MarkerAnnotation

Single valued Takes one value


Regular

@SingleValuedAnnotation("VALUE")

Takes more than one value @RegularAnnotation(name="John Q.", bugValue=1)

Table 1: Annotations get addressed differently depending upon the number of values they take.

Options

Description

none

Give no warnings.

unchecked

Give details on unchecked conversions of types.

path

Check for a nonexistent path in environment paths (such as classpath).

serial

Check that a serialVersionUID is given in serializable classes.

finally

Check that finally classes can be completed normally.

fallthrough

Make sure you break after each case statement that would otherwise fall through to the next case
statement in a switch.

depreciation Check for use of depreciated items.


Table 2: Options that can be given the -Xlint in javac.

com.sun.mirror.apt

Provides classes that interact directly with the tool and its data.

com.sun.mirror.declaration Defines interfaces to declarations.


com.sun.mirror.type

Defines interfaces to types defined within the source code.

com.sun.mirror.util

Contains utilities for processing types and declarations.

Table 3: Descriptions of the apt mirror API.

2/18/2012 5:37 PM

Potrebbero piacerti anche