jsr342-experts@javaee-spec.java.net

[jsr342-experts] Re: annotations vs. interfaces

From: Pete Muir <pmuir_at_bleepbleep.org.uk>
Date: Fri, 5 Oct 2012 12:55:59 -0700

On 4 Oct 2012, at 15:36, Bill Shannon wrote:

> 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.)

We agree entirely with this specific example. Kev (our batch EG rep) and I have recently discussed this exact point about the Batch Draft, and I know he took that feedback to the Batch EG.

>
> 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.

Agreed. A classic example of where we got this wrong is method interceptors/around invoke - it's a nightmare to remember the correct signature for around invoke methods (lifecycle callbacks defined on interceptors have the same issue).

>
> - To configure the container to use a particular implementation of such
> an interface in a particular situation, use an annotation.

+1

>
> - 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.

I think there is also something to be said around whether you are going to use the functionality in your business classes (your POJO model) or whether you are going to do this in a separate class. For example, to go back to interceptors, whilst lifecycle callbacks defined on interceptor classes would probably be better done as an interface implementation, lifecycle callbacks defined on beans clearly need to declared using annotations.

I think there is also something to be said for addressing how the functionality is used:

1) If there must be exactly one occurrence of every method defined on the implementation class, then an interface is best
2) If some (but not all) methods are optional to implement, then perhaps an abstract class is best (not sure, annotations might be best here as well).
3) If you can have none, one or more occurrences of a method, then annotations are best