2


2

Javaの汎用ハンドラーを使用したイベント処理

一般に次のようなカスタムの反感/処理イベントシステムを作成しました。

イベントハンドラーインターフェイス:

public interface EventHandler{
}

基本イベントクラス:

public abstract class Event {
    public static Class Type { }
    public abstract void dispatch(H handler);
}

ハンドラーマネージャー:

public class HandlerManager {
    private Map> map = new HashMap>();
    public void register(Event.Type type, H handler) {
        if(map.get(type) == null) { map.put(type, new ArrayList()); }
        map.get(type).add(handler);
    }

    public void fire(Event event) {...}
    ...
}

そして、すべてがうまく機能していますが、私は次のようなイベントを使用したいです

public class DataChangeEvent extends Event> {
    public static final Type<?> TYPE = new Type<?>();
    D data;
    ...
    public void dispatch(DataChangeHandler handler) {
        handler.onDataChanged(this);
    }
    public D getData() { return data; }
}

public class DataChangeHandler extends EventHandler {
    void onDataChanged(DataChangeEvent event);
}

そして、StringsやIntegersなどのイベントを生成するマネージャーにハンドラーDataChangeHandlerを登録すると、この登録済みハンドラーは両方のイベントを受け取り、データの読み取り時にClassCastExceptionが発生します。 ジェネリックには特別なクラスがなく、DataChangeHandlerで型が定義されているにもかかわらず、ハンドラマップの同じリストに格納されていることを理解しています。

機能させる方法はありますか?

2 Answer


4


これは本当に、本当に臭いデザインのようです。 そのタイプのイベントを処理するクラスでイベントをタイプする必要があるのはなぜですか? それは逆です。 EventHandlerは、それが処理するイベントのタイプとともに入力する必要があります。

だから私はあなたが実際にやろうとしていることにはあまり従いませんでしたが、あなたは基本的にこれをやろうとしていると思います:

private Map, List> map;
public  void register(Class<? extends T> typeFilter, EventHandler handler) {
    map.get(typeFilter).add(handler);
}


//...later
//safe since we used a generic method to add
@SuppressWarnings("unchecked");
public void fire(Event<?> event) {
    for ( EventHandler handler : map.get(event.getClass()) ) {
        handler.onDataChanged(event);
    }
}

//or similarly:
@SuppressWarnings("unchecked");
public void fire(Event<?> event) {
    for ( Class<?> type : map.keySet() ) {
        if ( !type.instanceOf(event) ) continue;
        for ( EventHandler handler : map.get(type) ) {
            handler.onDataChanged(event);
        }
    }
}

このタイプの設計は、ハンドラーが処理できないイベントを除外します。


3


ジェネリックは、主にコンパイル時の機能です。 実行時に型が必要な場合、引数としてクラスを渡し、フィールドに格納する必要があります。

私見:ディスパッチャーを作成するよりエレガントな方法は、注釈を使用することです。 Like

@EventHandler
public void onMyEvent(MyEvent event) {
   // is called when MyEvent is dispacted.
}