使用java中的通用处理程序处理事件

时间:2010-08-25 18:48:46

标签: java events generics handler

我编写的自定义调度/处理事件系统通常如下所示:

事件处理程序接口:

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 个答案:

答案 0 :(得分:4)

这看起来像是一个真正的,非常臭的设计。为什么要使用处理该类型事件的类来键入事件?那是倒退。应该使用它处理的事件类型键入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);
        }
    }
}

此类设计将过滤掉处理程序无法处理的事件。

答案 1 :(得分:3)

泛型主要是编译时功能。如果在运行时需要类型,则需要将类作为参数传递并将其存储在字段中。

恕我直言:创建调度程序的一种更为优雅的方法是使用注释。像

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