jsr342-experts@javaee-spec.java.net

[jsr342-experts] annotations vs. interfaces

From: Bill Shannon <bill.shannon_at_oracle.com>
Date: Thu, 04 Oct 2012 15:36:43 -0700

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.