Here is what I will do. If you can use Guava (a Google library written and used by Google), I recommend that you first scroll and look at another solution.
Vanilla java
First, start by adding a method to get the class from your subscribers:
public interface Subscriber<N extends News> { void onNews(N news); Class<N> getSupportedNewsType(); }
Then upon implementation:
public class MySubscriber implements Subscriber<MyNews> {
In the aggregator, turn on the card in which the keys and values ββare not entered:
private Map<Class<?>, Set<Subscriber<?>> subscribersByClass = ... ;
Also note that Guava has a multi-mode implementation that will use this key for several things you will need. Just google "guava multimap" and you will find it.
To register a subscriber:
public <N extends News> void register(Subscriber<N> subscriber) {
And send:
@SuppressWarnings("unchecked"); public <N extends News> void dispatch(N news) { Set<Subscriber<?>> subs = subscribersByClass.get(news.getClass()); if (subs == null) return; for (Subscriber<?> sub : subs) { ((Subscriber<N>) sub).onNews(news); } }
Pay attention to the cast here. This will be safe due to the nature of the generics between the register method and the Subscriber interface if no one does something ridiculously wrong, such as raw-typing, such as implements Subscriber (there is no common argument). Summary SuppressWarnings suppresses warnings about this cast from the compiler.
And your private method for extracting subscribers:
private Set<Subscriber<?>> getSubscriberSet(Class<?> clazz) { Set<Subscriber<?>> subs = subscribersByClass.get(news.getClass()); if (subs == null) { subs = new HashSet<Subscriber<?>>(); subscribersByClass.put(subs); } return subs; }
Your private methods and fields should not be type safe. In any case, this will not cause any problems, since Java generators are implemented using erasure, so all sets here will be just a set of objects. An attempt to make them safe types will only lead to unpleasant, unnecessary throws that have nothing to do with its correctness.
The important thing is that your public methods are type safe. The way to declare generics in Subscriber and the public methods on Aggregator , the only way to break it down is to use raw types, as I said above. In short, each Subscriber passed to the register is guaranteed to accept the types that you register it until unsafe discards or raw typing are detected.
Using Guava
Alternatively, you can take a look at the Guava EventBus . That would be easier, IMO, for what you are trying to do.
The Guava EventBus class uses annotation-dependent event dispatching instead of an interface driven one. It is very simple. You will no longer have the Subscriber interface. Instead, your implementation will look like this:
public class MySubscriber {
The @Subscribe annotation @Subscribe for the Guava EventBus that it must remember this method later for sending. Then, to register it and send events, use EventBus isntance:
public class Aggregator { private EventBus eventBus = new EventBus(); public void register(Object obj) { eventBus.register(obj); } public void dispatch(News news) { eventBus.dispatch(news); } }
This will automatically find the methods that accept the news object and send you a message. You can even subscribe more than once in the same class:
public class MySubscriber {
Or for several types inside the same subscriber:
public class MySubscriber {
Finally, the EventBus is a hierarchical structure, so if you have a class that extends MyNews , such as MyExtendedNews , then sending MyExtendedNews events MyExtendedNews also be sent to those who care about MyNews events. The same goes for interfaces. This way you can even create a global subscriber:
public class GlobalSubscriber {