Is using @annotations to tag listener methods rather than an interface w/ methods a good idea?
Firstly yes - this is subjective.
I have noticed lately a few libraries seem to be using the let the user tag listener methods in some class as listeners using different annotations to note different events. Infinispan and WELD come to mind as examples of this approach.
WHAT I HATE ABOUT THIS PATTERN
- no type safety
- need to read doco to find out proper type signatures for events rather than just implementing.
- for the implementator its extra messy as they need a lot more support code to discover and validate a submitted listener class, hold onto the method and dispatch it etc.
- the reflection dispatching thing makes firing event开发者_C百科s slower. Sure one could generate a real class using ASM but again that's a lot more work as compared to firing a simple listener.onEvent...
POSSIBLE REASONS
- makes it possible to have fine grained listeners. There is no need for empty do nothing methods if one has an uber listener interface. The counter argument is to not have an uber listener interface but rather have lots of one method interfaces.
OPINIONS
So why would anyone want to use this approach ? I personally don't get it, it seems to make more work for the implementator, makes life harder for the user, is slower, so why use it ?
Maybe an interesting use case is that the guys from the Servlet 3.0 spec wrestled a lot with this exact problem. The earliest proposals did away with interfaces like e.g. ServletContextListener and relied solely on annotations.
As you indicated, the type safety can be a problem here. It's awkward to guess what the exact signature of a method should be.
For among others because of this reason, the proposal was fiercely shut down by the community and they kept interfaces. Now the required registration in web.xml can be done with annotations, but the definition of the listeners methods are still via interfaces.
Annotations do have their advantages. Fine grained listeners are indeed one thing. You can't always counter this with fine grained one method interfaces. Suppose I have a (listener) interface with 6 methods. Most people only need 1 method, so you decide to break it up in 6 interfaces with each having one method. Now I'm the lucky guy who actually needs all 6 of those. In my class I must now declare I implement 6 interfaces.
I'm pretty sure a lot of people are not going to like that.
With annotations you can always mix and match the exact amount that you need.
Also, with annotations it's trivial to have multiple methods tagged in the same class. You can't do this with interfaces. There is only 1 method that implements a method from an interface, no more and no less.
Yet another 'advantage' of annotations is that since they don't exactly define what they apply to, one can support additional method signatures later. With interfaces you would have to add another interface. In my 6 interface example, suppose you later on decide you also need to support methods with 1 additional parameter, you would need 6 extra interfaces. This can add up fast.
A hybrid solution between interfaces and annotations could be the ability for annotations to specify one of more required method signatures. If such a signature would be defined, the annotation can then only be applied to a method that complies with it.
I use this approach to have listeners which can accept a specific type, an interface (which means all class this implement that interface) and an array of either.
e.g.
// called for each MyRecord
@Listener
public void onMyRecord(MyRecord r);
// when a batch of MyRecord is published,
// call this method on the swing event thread.
@Listener(swing=true)
public void onMyRecords(MyRecord... rs)
// Listeners can have priority to determine the order of multiple listeners
// Called when any number of events are published with a priority of 5.
@Listener(priority=5)
public void onIMyEvent(IMyEvent ... events)
精彩评论