Обработка событий с универсальными обработчиками в Java - PullRequest
2 голосов
/ 25 августа 2010

Я написал пользовательскую систему обработки событий / событий, которая обычно выглядит следующим образом:

Интерфейс обработчика событий:

public interface EventHandler{
}

Базовый класс событий:

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

Менеджер обработчиков:

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

    public void fire(Event<H> event) {...}
    ...
}

И все работает нормально, но я хочу использовать такие события, как

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

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

, и теперь, когда я регистрирую обработчик DataChangeHandler с менеджером, который генерирует события для строк и, например,для целых чисел этот зарегистрированный обработчик получит оба события, которые вызывают ClassCastException, когда я хочу прочитать данные.Я понимаю, что у дженериков нет специального класса и что, несмотря на тип, определенный в DataChangeHandler, они хранятся в одном и том же списке на карте обработчиков.

Есть ли способ заставить его работать?

Ответы [ 2 ]

4 голосов
/ 25 августа 2010

Это похоже на очень, очень вонючий дизайн.Почему событие должно быть напечатано с помощью класса, который обрабатывает событие такого типа?Это в обратном направлении.EventHandler должен быть набран с типом событий, которые он обрабатывает.

Так что я не совсем понял, что вы на самом деле пытаетесь сделать, но я думаю, что вы в основном пытаетесь это сделать:

private Map<Class<?>, List<EventHandler>> map;
public <T> void register(Class<? extends T> typeFilter, EventHandler<T> 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 голосов
/ 25 августа 2010

Обобщения являются в значительной степени функцией времени компиляции. Если вам нужен тип во время выполнения, вам нужно передать класс в качестве аргумента и сохранить его в поле.

ИМХО: более удобный способ создания диспетчера - использовать аннотацию. Как

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