これが私がすることです。Guava (Google によって作成および使用されている Google ライブラリ) を使用できる場合は、下にスクロールして、最初に他のソリューションを確認することをお勧めします。
バニラジャワ
まず、サブスクライバーからクラスを取得するメソッドを追加することから始めます。
public interface Subscriber<N extends News> {
void onNews(N news);
Class<N> getSupportedNewsType();
}
次に、実装する場合:
public class MySubscriber implements Subscriber<MyNews> {
// ...
public Class<MyNews> getSupportedNewsType() {
return MyNews.class;
}
}
アグリゲーターに、キーと値が入力されていないマップを含めます。
private Map<Class<?>, Set<Subscriber<?>> subscribersByClass = ... ;
また、Guavaには、このキーを複数の値に変換するマルチマップ実装があることに注意してください。「Guava Multimap」をグーグルで検索するだけで見つけることができます。
サブスクライバーを登録するには:
public <N extends News> void register(Subscriber<N> subscriber) {
// The method used here creates a new set and puts it if one doesn't already exist
Set<Subscriber<?>> subscribers = getSubscriberSet(subscriber.getSupportedNewsType());
subscribers.add(subscriber);
}
そしてディスパッチするには:
@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);
}
}
ここでキャストに注目。これは、 (ジェネリック引数なし)などの raw-typing のようなばかげた間違いを誰も行わない限り、registerメソッドとインターフェイスの間のジェネリックの性質により安全です。注釈は、コンパイラからのこのキャストに関する警告を抑制します。Subscriberimplements SubscriberSuppressWarnings
そして、サブスクライバーを取得するためのプライベート メソッド:
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;
}
privateメソッドとフィールドはタイプ セーフである必要はありません。いずれにせよ、Java のジェネリックは消去によって実装されるため、問題は発生しません。したがって、ここでのセットはすべてオブジェクトのセットにすぎません。それらを型安全にしようとすると、その正確性に関係のない厄介で不必要なキャストにつながるだけです。
重要なのは、publicメソッドがタイプ セーフであることです。ジェネリックが で宣言されSubscriber、パブリック メソッドが で宣言されてAggregatorいる方法を破る唯一の方法は、上で述べたように生の型を使用することです。Subscriberつまり、レジスタに渡されたすべての型は、安全でないキャストや生の型付けがない限り、登録している型を受け入れることが保証されています。
グアバの使用
または、グアバのEventBus. これは、あなたがやろうとしていることに対して、IMOより簡単です。
Guava のEventBusクラスは、インターフェイス駆動型ではなく、アノテーション駆動型のイベント ディスパッチを使用します。それは本当に簡単です。Subscriberもうインターフェースはありません。代わりに、実装は次のようになります。
public class MySubscriber {
// ...
@Subscribe
public void anyMethodNameYouWant(MyNews news) {
// Handle news
}
}
アノテーションは、後でディスパッチするためにそのメソッドを覚えておく必要があること@Subscribeを Guava に通知します。EventBus次に、それを登録してイベントをディスパッチするには、インスタンスを使用しEventBusます。
public class Aggregator {
private EventBus eventBus = new EventBus();
public void register(Object obj) {
eventBus.register(obj);
}
public void dispatch(News news) {
eventBus.dispatch(news);
}
}
newsこれにより、オブジェクトを受け入れてディスパッチを行うメソッドが自動的に検出されます。同じクラスで複数回サブスクライブすることもできます。
public class MySubscriber {
// ...
@Subscribe
public void anyMethodNameYouWant(MyNews news) {
// Handle news
}
@Subscribe
public void anEntirelyDifferentMethod(MyNews news) {
// Handle news
}
}
または、同じサブスクライバー内の複数のタイプの場合:
public class MySubscriber {
// ...
@Subscribe
public void handleNews(MyNews news) {
// Handle news
}
@Subscribe
public void handleNews(YourNews news) {
// Handle news
}
}
最後に、EventBusは階層構造を尊重するためMyNews、 などのを拡張するクラスがある場合MyExtendedNews、ディスパッチMyExtendedNewsイベントはイベントを処理するクラスにも渡されMyNewsます。インターフェイスについても同様です。このようにして、グローバル サブスクライバーを作成することもできます。
public class GlobalSubscriber {
// ...
@Subscribe
public void handleAllTheThings(News news) {
// Handle news
}
}