Вы находитесь на странице: 1из 17

Annotations in Tiger, Part 1: Add metadata

to Java code
One of the latest trends in programming, particularly in Java programming, is the use of
metadata. Metadata, simply put, is data about data. Metadata can be used to create
documentation, to track down dependencies in code, and even to perform rudimentary compiletime checking. A rash of tools for metadata, such as XDoclet (see Resources), add these features
to the core Java language and have been part of the Java programming landscape for a while.
Until the availability of J2SE 5.0 (aka Tiger, now in its second beta release), the core Java
language came closest to a metadata facility with the Javadoc methodology. You use a special set
of tags to mark up your code and then execute the javadoc command to turn the tags into a
formatted HTML page that documents the classes the tags are attached to. Javadoc is an
inadequate metadata tool, though, because you have no solid, practical, standardized way to get
at the data for any purpose other than generating documentation. The fact that HTML code is
often mixed into the Javadoc output decreases its value for any other purpose even further.
Tiger incorporates a far more versatile metadata facility into the core Java language through a
new feature called annotations. Annotations are modifiers you can add to your code and apply to
package declarations, type declarations, constructors, methods, fields, parameters, and variables.
Tiger includes built- in annotations and also supports custom annotations you can write yourself.
This article will give you an overview of metadata's benefits and introduce you to Tiger's built- in
annotations. Part 2 in this article series will explore custom annotations. My thanks to O'Reilly
Media, Inc., which has graciously allowed me to use the code sample from the annotations
chapter of my book on Tiger for this article (see Resources).
The value of metadata
In general, metadata's benefits fall into three categories: documentation, compiler checking, and
code analysis. Code-level documentation is the most-often-cited use. Metadata provides a helpful
way to indicate if methods are dependent on other methods, if they are incomplete, if a certain
class must reference another class, and so on. This is indeed useful, but documentation is
probably the least relevant reason for adding metadata to the Java language. Javadoc already
provides a fairly easy-to-understand and robust way to document code. Besides, who wants to
write a documentation tool when one already exists and works fine for the most part?

Don't miss the rest of this series


Be sure to read, "Part 2" of this series, which explores custom annotations.
Compiler checking
A more significant advantage of metadata is the ability for a compiler to use it to perform some
basic compile-time checking. For example, you'll see in The Override annotation, later in this

article, that Tiger introduces an annotation that lets you specify that a method overrides another
method from a superclass. The Java compiler can ensure that the behavior you indicate in your
metadata actually happens at a code level. This might seem silly if you've never chased down
this type of bug, but most grizzled Java programming veterans have spent at least a few long
nights trying to discover why their code doesn't work. When you finally realize that a method has
a parameter wrong, and in fact isn't overriding a method from a superclass, much bitterness can
result. A tool that consumes metadata can help you discover this type of error easily and save
those nights for long-running Halo tournaments.

JSR 175
JSR 175, A Metadata Facility for the Java Programming Language, provides the official
justification and specification for the incorporation of metadata into the core Java language (see
Resources). According to the JSR, annotations "do not directly affect the semantics of a program.
Development and deployment tools can, however, read these annotations and process them in
some fashion, perhaps producing additional Java programming language source files, XML
documents, or other artifacts to be used in conjunction with the program containing the
annotations."
Code analysis
Arguably, the nicest feature of any good annotation or metadata tool is the ability to use the extra
data to analyze code. In a simple case, you might build up a code catalog, provide required input
types, and indicate return types. But -- you're probably thinking -- Java reflection offers the same
benefits; after all, you can introspect code for all of this information. That might seem true on the
surface, but it doesn't always hold in practice. Many times, a method accepts as input or returns
as output a type that is actually not what the method wants. For example, the parameter type
might be Object , but the method works only with (again, as an example) Integers. This can
happen easily in cases in which methods are being overridden and the superclass declares the
method with generic parameters, or in a system where lots of serialization is going on. In both
cases, metadata can instruct a code-analysis tool that although the parameter type is Object, an
Integer is what's really desired. This sort of analysis is incredibly useful, and its value can't be
overstated.
In more-complex cases, code-analysis tools can perform all sorts of extra tasks. The example du
jour is Enterprise JavaBean (EJB) components. The dependencies and complexities in even
simple EJB systems are almost staggering. You've got home and remote interfaces, along with
the possibility of local and local home interfaces, as well as an implementation class. Keeping
these classes all in sync is a royal pain. Metadata, though, can provide a solution for this
problem. A good tool (again, XDoclet is worth mentioning here) can manage all of these
dependencies, ensuring that classes that have no "code- level" connection, but do have a "logiclevel" tie-in, stay in sync. It's here that metadata can truly shine.
Back to top
Annotation basics

Now that you have a grasp of the things metadata is good for, I'll introduce you to annotations in
Tiger. Annotations take the form of an "at" sign (@ ), followed by the annotation name. Then, you
supply data to the annotation -- when data is required -- in name=value pairs. Each time you use
this sort of notation, you're making an annotation. One piece of code might have 10, 50, or more
annotations. However, you'll find that several of your annotations might all use the same
annotation type. The type is the actual construct used, and the annotation itself is the specific
usage of that type, in a particular context (see the sidebar, Annotation or annotation type?).

Annotation or annotation type?


Confused about what's an annotation versus an annotation type? The easiest way to get this
straight is to think in terms of Java language concepts you're already familiar with. You can
define a single class (for example, Person), and you will always have only one version of the
class in the JVM (assuming you aren't doing nasty class path things). However, you might have
10 or 20 instances of that class in use at any given time. There's still only a single Person class,
but it's used multiple times, in varying ways. The same is true of annotation types and
annotations. An annotation type is analogous to the class, and an annotation is analogous to an
instance of that class.
Annotations fall into three basic categories:

Marker annotations have no variables. The annotation simply appears, identified by name, with
no additional data supplied. For example, @MarkerAnnotation is a marker annotation. It
includes no data, just the annotation name.

Single-value annotations are similar to markers, but provide a single piece of data. Because only
a single bit of data is supplied, you can use a shortcut syntax (assuming the annotation type is
defined to accept this syntax): @SingleValueAnnotation("my data"). This should look a
lot like a normal Java method call, aside from the @ sign.

Full annotations have multiple data members. As a result, you must use a fuller syntax (and the
annotation doesn't look quite so much like a normal Java method anymore):
@FullAnnotation(var1="data value 1", var2="data value 2", var3="data
value 3").

In addition to supplying values to annotations through the default syntax, you can use namevalue pairs when you need to pass in more than one value. You can also supply arrays of values
to annotation variables, through curly braces. Listing 1 shows an example an array of values in
an annotation.
Listing 1. Using arrayed values in annotations
@TODOItems({
// Curly braces indicate an array of values is being supplied
@TODO(
severity=TODO.CRITICAL,
item="Add functionality to calculate the mean of the student's grades",
assignedTo="Brett McLaughlin"

),
@TODO(
severity=TODO.IMPOTANT,
item="Print usage message to screen if no command-line flags specified",
assignedTo="Brett McLaughlin"
),
@TODO(
severity=TODO.LOW,
item="Roll a new website page with this class's new features",
assignedTo="Jason Hunter"
)
})

The example in Listing 1 is simpler than it might look at first glance. The TODOItems annotation
type has a single variable that takes a value. The value supplied here is fairly complex, but the
use of TODOItems actually matches the single-value annotation style -- except that the single
value is an array. The array contains three TODO annotations, each of which is multivalued.
Commas separate the values within each annotation, as well as the value within a single array.
Easy enough, right?
But I'm getting ahead of myself a bit. TODOItems and TODO are custom annotations, the topic of
Part 2 of this series. But I wanted you to see that even a complex annotation -- and Listing 1 is
almost as complex as you can make any annotation -- isn't all that daunting. When it comes to
the Java language's standard annotation types, you'll rarely see anything so convoluted. As you'll
learn in the next three sections, Tiger's basic annotation types are extremely simple to use.
Back to top
The Override annotation
Tiger's first built- in annotation type is Override. Override should be used only on methods (not
on classes, package declarations, or other constructs). It indicates that the annotated method is
overriding a method in a superclass. Listing 2 shows a simple example.
Listing 2. The Override annotation in action
package com.oreilly.tiger.ch06;
public class OverrideTester {
public OverrideTester() { }
@Override
public String toString() {
return super.toString() + " [Override Tester Implementation]";
}
@Override
public int hashCode() {

return toString().hashCode();
}
}

Listing 2 should be pretty easy to follow. The @Override annotation annotates two methods -toString() and hashCode() -- to indicate that they override versions of the methods from the
OverrideTester class's superclass (java.lang.Object). This might seem trivial at first, but it's
actually a nice feature. You literally cannot compile this class without overriding these methods.
The annotation also ensures that when you mess with toString(), you also have at least some
indication that you should make sure that hashCode() still matches up.
This annotation type really shines when you're up too late coding and mistype something, as in
Listing 3.
Listing 3. Letting the Override annotation catch typos
package com.oreilly.tiger.ch06;
public class OverrideTester {
public OverrideTester() { }
@Override
public String toString() {
return super.toString() + " [Override Tester Implementation]";
}
@Override
public int hasCode() {
return toString().hashCode();
}
}

In Listing 3, hashCode() is mistyped as hasCode(). The annotation indicates that hasCode()


should override a method. But in compilation, javac will realize that the superclass (again,
java.lang.Object) has no method named hasCode() to override. As a result, the compiler
gives you an error, like the one shown in Figure 1.

Figure 1. Compiler warning from Override annotation

Missing functionality
It would be nice if Deprecated let you include an error-type message, in the single-value
annotation style. The compiler could then print the message when users use the deprecated
method. The message could indicate how serious the consequences of using the method are, say
when the method will be phased out, and even suggest alternatives. Alas, maybe this will come
with the next J2SE version ("Mustang," they're calling that one now).
This handy little feature will help you catch typos very quickly.
Back to top
The Deprecated annotation
The next standard annotation type is Deprecated. Like Override, Deprecated is a marker
annotation. As you might expect, you use Deprecated to annotate a method that shouldn't be
used anymore. Unlike Override, Deprecated should be placed on the same line as the method
being deprecated (why? I'm honestly not sure), as in Listing 4.
Listing 4. Using the Deprecated annotation
package com.oreilly.tiger.ch06;
public class DeprecatedClass {
@Deprecated public void doSomething() {
// some code

}
public void doSomethingElse() {
// This method presumably does what doSomething() does, but better
}
}

You shouldn't expect anything unusual to happen when you compile this class on its own. But if
you then use the deprecated method, either by overriding it or invoking it, the compiler processes
the annotation, realizes that the method shouldn't be used, and issues an error message, as in
Figure 2.
Figure 2. Compiler warning from a Deprecated annotation

Note that you need to turn on the compiler warnings, just as you must to indicate to the Java
compiler that you want normal deprecation warnings. You can use one of two flags with the
javac command: -deprecated or the new -Xlint:deprecated flag.
Back to top
The SuppressWarnings annotation
The last annotation type that you get "for free" with Tiger is SuppressWarnings. You shouldn't
have any trouble figuring out what this one does, but it's not always obvious why this annotation
type is so important. It's actually a side-effect of Tiger's all- new set of features. For example,
consider generics; generics make all sorts of new type-safe operations possible, especially when
it comes to Java collections. However, because of generics, the compiler now throws warnings
when collections are used without type safety. That's helpful for code aimed at Tiger, but it

makes writing code intended for Java 1.4.x or earlier a real pain. You'll constantly receive
warnings about things that you're not at all concerned about. How can you get the compiler to
leave you in peace?
comes to the rescue. SupressWarnings, unlike Override and Deprecated,
does have a variable -- so you use the single-annotation style for working with it. You can supply
the variable as an array of values, each of which indicates a specific type of warning to suppress.
Take a look at the example in Listing 5, which is some code that normally generates an error in
Tiger.
SupressWarnings

Listing 5. Tiger code that isn't type-safe


public void nonGenericsMethod() {
List wordList = new ArrayList();
wordList.add("foo");

// no typing information on the List


// causes error on list addition

Figure 3 shows the result of compiling the code in Listing 5.


Figure 3. Compiler warning from non-typed code

Listing 6 gets rid of this pesky warning by employing the SuppressWarnings annotation.
Listing 6. Suppressing a warning

@SuppressWarnings(value={"unchecked"})
public void nonGenericsMethod() {
List wordList = new ArrayList();
// no typing information on the List
wordList.add("foo");

// causes error on list addition

Simple enough, right? Just locate the type of warning (appearing in Figure 3 as "unchecked"),
and pass it in to SuppressWarnings.
The fact that the value of the variable in SuppressWarnings takes an array lets you suppress
multiple warnings in the same annotation. For example,
@SuppressWarnings(value={"unchecked", "fallthrough"}) takes a two- value array. This
facility provides a pretty flexible means of handling errors without requiring you to be overly
verbose.
Back to top
Conclusion
Although the syntax you've seen here might be new, you should be thinking that annotations are
pretty easy to understand and use. That said, the standard annotation types that come with Tiger
are rather bare-boned and leave much to be desired. Metadata is increasingly useful, and you'll
certainly come up with annotation types that are perfect for your own applications. In Part 2 of
this series. I'll detail Tiger's support for writing your own annotation types. You'll learn how to
create a Java class and define it as an annotation type, how to let the compiler know about your
annotation type, and how to use it to annotate your code. I'll even dig a bit into the bizarresounding but useful task of annotating annotations. You'll quickly master this new construct in
Tiger.
In the first article in this series, I explained what metadata is, why it's valuable, and how to use
the basic built- in annotations introduced in J2SE 5.0 (aka Tiger). If you're comfortable with these
concepts now, you might already be thinking that the three standard annotations Java 5 offers
aren't especially robust. You can do only so much with Deprecated, SuppressWarnings, and
Override. Fortunately, Tiger also lets you define your own annotation types. In this article, I'll
take you through this relatively simple process with some examples. You'll also find out how to
annotate your own annotations and what you gain by doing so. My thanks to O'Reilly Media,
Inc., which has graciously allowed me to use the code sample from the annotations chapter of my
book on Tiger for this article (see Resources).
Defining your own annotation type
With the addition of a little syntax (and Tiger has added plenty of syntactical constructs), the
Java language supports a new type -- the annotation type. An annotation type looks a lot like an
ordinary class, but it has some unique properties. Most notably, you can use it with the at sign (@ )
in your classes to annotate your other Java code. I'll walk you through the process piece by piece.

The @interface declaration


Defining a new annotation type is a lot like creating an interface, except that you precede the
interface keyword with the @ sign. Listing 1 shows an example of the simplest possible
annotation type:
Listing 1. A very simple annotation type
package com.oreilly.tiger.ch06;
/**
* Marker annotation to indicate that a method or class
*
is still in progress.
*/
public @interface InProgress { }

Listing 1 is pretty self-explanatory. If you compile this annotation type and ensure that it's in
your classpath, you can then use it on your own source code methods to indicate that a method or
class is still in progress, as in Listing 2:
Listing 2. Using your custom annotation type
@com.oreilly.tiger.ch06.InProgress
public void calculateInterest(float amount, float rate) {
// Need to finish this method later
}

You use the annotation type in Listing 1 exactly the same way you use the built- in annotation
types, except that you indicate the custom annotation by both its name and package. Of course,
normal Java rules apply, so you can import the annotation type and refer to it as simply
@InProgress.

Don't miss the rest of this series


Be sure to read, "Part 1" of this series, which introduces annotations in Java 5.0.
Adding a member
The basic usage I've just shown you is far from robust. As you'll remember from Part 1,
annotation types can have member variables (see Resources). This is useful, especially when you
begin to use annotations as more-sophisticated metadata, not just raw documentation. Codeanalysis tools like to have lots of information to crunch, and custom annotations can supply that
information.

Data members in annotation types are set up to work using limited information. You don't define
a member variable and then provide accessor and mutator methods. Instead, you define a single
method, named after the member, that you want to allow for. The data type should be the return
value of the method. The concrete example in Listing 3 should make this somewhat confusing
requirement more clear:
Listing 3. Adding a member to an annotation type
package com.oreilly.tiger.ch06;
/**
* Annotation type to indicate a task still needs to be
*
completed.
*/
public @interface TODO {
String value();
}

As odd as Listing 3 might look, it's what you need in annotation types. Listing 3 defines a string
named value that the annotation type can accept. You then use the annotation type as in Listing
4:
Listing 4. Using an annotation type with a member value
@com.oreilly.tiger.ch06.InProgress
@TODO("Figure out the amount of interest per month")
public void calculateInterest(float amount, float rate) {
// Need to finish this method later
}

Again, not much is tricky here. Listing 4 assumes that com.oreilly.tiger.ch06.TODO has
been imported, so in the source, you don't prefix the annotation with its package name. Also note
that Listing 4 uses the shorthand approach: You feed the value ("Figure out the amount of
interest per month") into the annotation without specifying the member-variable name.
Listing 4 is equivalent to Listing 5, which doesn't use the shorthand:
Listing 5. "Longhand" version of Listing 4
@com.oreilly.tiger.ch06.InProgress
@TODO(value="Figure out the amount of interest per month")
public void calculateInterest(float amount, float rate) {
// Need to finish this method later
}

Of course, we're all coders, so who wants to mess with the longhand version? Take note, though
-- the shorthand version is available only if the annotation type has a single-member variable
named value. If you don't meet this condition, you lose the shorthand feature.
Setting default values
What you've seen so far is a good start, but you have plenty of ways to spruce it up. Probably the
next addition you'll think of is to set some default values for the annotation. This is nice when
you want users to specify some values, but they need to specify other values only if they differ
from the default. Listing 6 illustrates both this concept and its implementation with another
custom annotation -- a fuller- featured version of the TODO annotation type from Listing 4:
Listing 6. Annotation type with default values
package com.oreilly.tiger.ch06;
public @interface GroupTODO {
public enum Severity { CRITICAL, IMPORTANT, TRIVIAL, DOCUMENTATION };
Severity severity() default Severity.IMPORTANT;
String item();
String assignedTo();
String dateAssigned();
}

The GroupTODO annotation type in Listing 6 adds several new variables. Note that this annotation
type doesn't have a single- member variable, so you gain nothing by naming one of the variables
value. Any time you have more than one member variable, you should name them as precisely
as possible. You don't get the benefit of the shorthand syntax shown in Listing 5, so you might as
well be a little more verbose and create better self-documentation for your annotation type.
Another new feature that Listing 6 demonstrates is that the annotation type defines its own
enumeration. (Enumerations -- usually just called enums -- are another new feature of Java 5.
This isn't anything remarkable, or even specific to annotation types.) Then, Listing 6 uses the
new enumeration as the type for the member variable.
Finally, back to the subject at hand -- default values. Establishing them is pretty trivial. You add
the keyword default at the end of the member declaration, and then supply the default value. As
you might expect, this must be the same type that you declared for the member variable. Again,
this isn't rocket science -- just a little bit of a lexical twist. Listing 7 shows the GroupTODO
annotation in action, in a case in which severity is not indicated:
Listing 7. Taking advantage of default values

@com.oreilly.tiger.ch06.InProgress
@GroupTODO(
item="Figure out the amount of interest per month",
assignedTo="Brett McLaughlin",
dateAssigned="08/04/2004"
)
public void calculateInterest(float amount, float rate) {
// Need to finish this method later
}

Listing 8 shows the same annotation in use, this time with a value supplied for severity :
Listing 8. Overriding default values
@com.oreilly.tiger.ch06.InProgress
@GroupTODO(
severity=GroupTODO.Severity.DOCUMENTATION,
item="Need to explain how this rather unusual method works",
assignedTo="Jon Stevens",
dateAssigned="07/30/2004"
)
public void reallyConfusingMethod(int codePoint) {
// Really weird code implementation
}

Back to top
Annotating an annotation
Before closing the book on annotations (at least in this series), I'll deal briefly with annotating
annotations. The set of predefined annotation types you learned about in Part 1 have a
predetermined purpose. However, as you move into writing your own annotation types, the
purpose of your annotation types isn't always self-evident. In addition to basic documentation,
you'll probably write types that are specific to a certain member type, or perhaps a certain set of
member types. This requires you to supply some sort of metadata on your annotation type, so
that the compiler can enforce the annotation's intended functionality.
Of course, annotations -- the Java language's choice for metadata -- should immediately come to
mind as the solution. You can use four predefined annotation types -- referred to as metaannotations -- to annotate your annotations. I'll cover each one in turn.
Specifying the target
The most obvious meta-annotation is one that allows you to indicate which program elements
can have annotations of the defined type. Unsurprisingly, this meta-annotation is called Target.
Before you see how to use Target, though, you need to know about another new class -- actually

an enum -- called ElementType. This enum defines the various program elements that an
annotation type can target. Listing 9 show the ElementType enum in its entirety:
Listing 9. The ElementType enum
package java.lang.annotation;
public enum ElementType {
TYPE,
//
FIELD,
//
METHOD,
//
PARAMETER,
//
CONSTRUCTOR,
//
LOCAL_VARIABLE,
//
ANNOTATION_TYPE,
//
PACKAGE
//
}

Class, interface, or enum (but not annotation)


Field (including enumerated values)
Method (does not include constructors)
Method parameter
Constructor
Local variable or catch clause
Annotation Types (meta-annotations)
Java package

The enumerated values in Listing 9 are pretty obvious, and you can figure out on your own (with
help from the comments) how each one applies. When you use the Target meta-annotation, you
supply it at least one of these enumerated values and indicate which program elements the
annotated annotation can target. Listing 10 shows Target in action:

Listing 10. Using the Target meta-annotation


package com.oreilly.tiger.ch06;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Annotation type to indicate a task still needs to be completed
*/
@Target({ElementType.TYPE,
ElementType.METHOD,
ElementType.CONSTRUCTOR,
ElementType.ANNOTATION_TYPE})
public @interface TODO {
String value();
}

Now the Java compiler will apply TODO only to types, methods, constructors, and other
annotation types. This helps you ensure that nobody else takes your annotation type and
misapplies it (or, better yet, that you don't misapply it in a fit of fatigue).
Setting retention

The next meta-annotation you want to get under your fingers is Retention. This metaannotation is related to how the Java compiler treats the annotated annotation type. The compiler
has several options:

Retain the annotation in the compiled class file of the annotated class, and read it when the
class first loads
Retain the annotation in the compiled class file, but ignore it at runtime
Use the annotation as indicated, but then discard it in the compiled class file

These three options are represented in the java.lang.annotation.RetentionPolicy enum,


shown in Listing 11:
Listing 11. The RetentionPolicy enum
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE,
// Annotation is discarded by the compiler
CLASS,
// Annotation is stored in the class file, but ignored
by the VM
RUNTIME
// Annotation is stored in the class file and read by
the VM
}

As you should expect by now, the Retention meta-annotation type takes as its single argument
one of the enumerated values you see in Listing 11. You target this meta-annotation to your
annotations, as shown in Listing 12:
Listing 12. Using the Retention meta-annotation
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
// annotation type body
}

As you can tell from Listing 12, you can use the shorthand form here, because Retention has a
single- member variable. And if you want the retention to be RetentionPolicy.CLASS, you don't
have to do a thing, because that's the default behavior.
Adding public documentation
The next meta-annotation is Documented. This is another one that's pretty easy to understand,
partly because Documented is a marker annotation. As you should remember from Part 1, marker
annotations have no member variables. Documented indicates that an annotation should appear in

the Javadoc for a class. By default, annotations are not included in Javadoc -- a fact worth
remembering when you spend a lot of time annotating a class, detailing what's left to do, what it
does correctly, and otherwise documenting its behavior.
Listing 13 shows what the Documented meta-annotation looks like in use:
Listing 13. Using the Documented meta-annotation
package com.oreilly.tiger.ch06;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Marker annotation to indicate that a method or class
*
is still in progress.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface InProgress { }

The one "gotcha" with Documented is in the retention policy. Notice that Listing 13 specifies the
annotation's retention as RUNTIME. This is a required aspect of using the Documented annotation
type. Javadoc loads its information from class files (not source files), using a virtual machine.
The only way to ensure that this VM gets the information for producing Javadoc from these class
files is to specify the retention of RetentionPolicy.RUNTIME. As a result, the annotation is kept
in the compiled class file and is loaded by the VM; Javadoc then picks it up and adds it to the
class's HTML documentation.
Setting up inheritance
The final meta-annotation, Inherited, is probably the most complicated to demonstrate, the
least-often used, and the one that creates the most confusion. All that said, let's cheerily run
through it.
First, take a use case: Suppose that you mark a class as being in progress, through your own
custom InProgress annotation. No problem, right? This will even show up in the Javadoc if
you've correctly applied the Documented meta-annotation. Now, suppose you write a new class
and extend the in-progress class. Easy enough, right? But remember that the superclass is in
progress. If you use the subclass, and even look at its documentation, you get no indication that
anything is incomplete. You would expect to see that the InProgress annotation is carried
through to subclasses -- that it's inherited -- but it isn't. You must use the Inherited metaannotation to specify the behavior you want, as shown in Listing 14:

Listing 14. Using the Inherited meta-annotation


package com.oreilly.tiger.ch06;
import
import
import
import

java.lang.annotation.Documented;
java.lang.annotation.Inherited;
java.lang.annotation.Retention;
java.lang.annotation.RetentionPolicy;

/**
* Marker annotation to indicate that a method or class
*
is still in progress.
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface InProgress { }

With the addition of @Inherited, you'll see the InProgress annotation show up on subclasses
of annotated classes. Of course, you don't really want this behavior on all your annotation types
(that's why the default is not to inherit); for example, the TODO annotation wouldn't (and
shouldn't) be propagated. Still, for the case I've shown here, Inherited can be quite helpful.
Back to top
Conclusion
At this point, you're ready to go back into the Java world and document and annotate everything.
Then again, this reminds me a bit of what happened when everyone figured out Javadoc. We all
went into the mode of over-documenting everything, before someone realized that Javadoc is
best used for clarification of confusing classes or methods. Nobody looks at those easy-tounderstand getXXX() and setXXX() methods you worked so hard to Javadoc.
The same trend will probably occur with annotations, albeit to a lesser degree. It's a great idea to
use the standard annotation types often, and even heavily. Every Java 5 compiler will support
them, and their behavior is well- understood. However, as you get into custom annotations and
meta-annotations, it becomes harder to ensure that the types you work so hard to create have any
meaning outside of your own development context. So be measured. Use annotations when it
makes sense to, but don't get ridiculous. However you use it, an annotation facility is nice to
have and can really help out in your development process.

Вам также может понравиться