Earlier this year I wrote up a bunch of our "rules" for how annotations
should work:
http://java.net/projects/javaee-spec/pages/AnnotationRules
While reviewing the Batch spec:
http://download.oracle.com/otndocs/jcp/batch-1_0-edr-spec/index.html
I noticed that they were using annotations in places that I expected
to see interfaces.
For example, they allow a class to be a "listener" for events, and they
declare that the class is a listener by annotating the class. To indicate
which method of the class should be called when the event occurs, they
annotate the method. The different events are a finite and fixed set,
and each type of listener handles only a few events associated with that
type. You might have:
@JobListener
public class MyJobListener {
@JobStarted
public void jobStarted() { ... }
@JobFinished
public void jobFinished() { ... }
}
To me, this just felt like an abuse of annotations in a place where a
listener interface is a widely used and well understood approach.
Why would you not simply do:
public interface JobListener {
public void jobStarted();
public void jobFinished();
}
public class MyJobListener implements JobListener {
public void jobStarted() { ... }
public void jobFinished() { ... }
}
Of course, you'll want an annotation on MyJobListener to configure
the listener and associate it with a specific job. (You don't want
a class to be used just because it's been declared.)
It seemed like it would be worth writing down some general principles
as an update to my annotation rules page above. Here's what I have so
far, but I'd love your feedback. Of course, it's likely that we haven't
followed a consistent set of rules for things we've already done, so there
will always be exceptions. Let me know if you agree with the following,
and especially let me know of any additions.
- When defining an API that an application is going to implement and
the container is going to call, use an interface.
- To configure the container to use a particular implementation of such
an interface in a particular situation, use an annotation.
- If an application class is providing an API that's exposed to other
applications, and that class also needs to provide methods that the
container will call for lifecycle functions, use an annotation to
mark those methods so that the application has flexibility in the
choice of names for those methods.
- If an application is going to expose an interface that another
application (or user) is going to use *without* Java, use annotations
to mark the methods that correspond to this interface. This avoids
the need to define a Java interface that's never going to be used by
anyone other than the one class implementing the interface.