是否可以在Java中创建我自己的包含多种侦听器类型的事件侦听器列表?

是否可以在Java中创建我自己的包含多种侦听器类型的事件侦听器列表?,java,addeventlistener,event-listener,type-erasure,Java,Addeventlistener,Event Listener,Type Erasure,我正在实现一个客户机-服务器系统,其中客户机处于一个连续的阻塞读取循环中,侦听来自服务器的消息。当收到消息时,我希望根据消息的类型引发一个“事件”,其他GUI类可能会向其中添加侦听器。我更熟悉C#events,所以我仍然习惯于Java的做事方式 将有许多消息类型,因此我需要为每种类型提供一个接口,称之为MessageTypeAListener、MessageTypeBListener等,其中每种类型都包含一个句柄方法,我的GUI类将实现该方法。但是,将有许多类型,我不想维护每种类型的侦听器列表,

我正在实现一个客户机-服务器系统,其中客户机处于一个连续的阻塞读取循环中,侦听来自服务器的消息。当收到消息时,我希望根据消息的类型引发一个“事件”,其他GUI类可能会向其中添加侦听器。我更熟悉C#events,所以我仍然习惯于Java的做事方式

将有许多消息类型,因此我需要为每种类型提供一个接口,称之为MessageTypeAListener、MessageTypeBListener等,其中每种类型都包含一个句柄方法,我的GUI类将实现该方法。但是,将有许多类型,我不想维护每种类型的侦听器列表,也不想有几个“fire”方法,而是希望有一个大的侦听器列表和一个类型化的fire方法。然后fire方法可以说“只有fire侦听器的类型是我指定的类型。”

例如(伪代码):

ListenerList.Add(MessageTypeAListener);
添加(MessageTypeBListener);
火警(讯息){
ListenerList.Where(类型为T).handle(消息)
}
...  
火灾(信息);

然而,类型擦除似乎使这变得困难。我可以尝试铸造和捕捉异常,但这似乎是错误的。是否有一种干净的方法来实现这一点,或者对于每种类型都保留一个单独的侦听器列表更明智,即使有很多类型?

老式的模式方法,使用:

更现代的方法只是在不应该相互派生的事件类和这些事件的各自处理程序之间建立映射。这样可以避免访问者模式的缺点(也就是说,每次添加新事件时,您都需要更改所有访问者类,至少更改它们的基础)

另一种方法是使用:

接口侦听器{
void handleEventA();
void handleEventB();
}
类ListenerOne实现Listener{
公共无效handleEventA(){
System.out.println(“eventA”);
}
公共无效handleEventB(){
//无所事事
}
}
类CompositeListener实现侦听器{
私有最终CopyOnWriteArrayList侦听器=新建CopyOnWriteArrayList();
void addListener(侦听器l){
如果(此!=l)
添加(l);
}
公共无效handleEventA(){
for(侦听器l:侦听器)
l、 handleEventA();
}
公共无效handleEventB(){
for(侦听器l:侦听器)
l、 handleEventB();
}
}

我实现了类似的东西,因为我对Java的EventListenerList有一种发自内心的厌恶。首先,实现一个通用侦听器。我根据侦听器接收的事件定义了侦听器,基本上只有一个方法

interface GenericListener<T extends Event> {
   public void handle(T t);
}
根据事件的类别,获取希望获取该事件的侦听器列表。
您的选择是基于侦听器的类

Map<Class<T extends MyListener>, Collection<MyListener>>
Map

上面可能有一些输入错误…

如果您只有简单的事件,即没有数据的事件或所有事件都有相同的数据类型,enum可能是一种前进的方式:

public enum Event {
    A,
    B,
    C
}

public interface EventListener {
    void handle(Event event);
}

public class EventListenerImpl implements EventListener {
    @Override
    public void handle(Event event) {
        switch(event) {
            case A: 
                // ...
                break;
        }
    }
}

public class EventRegistry {
    private final Map<Event, Set<EventListener>> listenerMap;

    public EventRegistry() {
        listenerMap = new HashMap<Event, Set<EventListener>>();
        for (Event event : Event.values()) {
            listenerMap.put(event, new HashSet<EventListener>());
        }
    }

    public void registerEventListener(EventListener listener, Event event) {
        Set<EventListener> listeners = listenerMap.get(event);
        listeners.add(listener);
    }

    public void fire(Event event) {
        Set<EventListener> listeners = listenerMap.get(event);
        for (EventListener listener : listeners) {
            listener.handle(event);
        }
    }
}
公共枚举事件{
A.
B
C
}
公共接口事件侦听器{
无效句柄(事件);
}
公共类EventListenerImpl实现EventListener{
@凌驾
公共无效句柄(事件){
开关(事件){
案例A:
// ...
打破
}
}
}
公共类事件注册表{
私有最终地图listenerMap;
公共事件注册表(){
listenerMap=新建HashMap();
对于(事件:Event.values()){
put(事件,newhashset());
}
}
public void registerEventListener(EventListener,Event-Event){
Set listeners=listenerMap.get(事件);
添加(侦听器);
}
公共空间火灾(事件){
Set listeners=listenerMap.get(事件);
for(EventListener:侦听器){
句柄(事件);
}
}
}
评论:

如果
EventListnerImpl
中的
switch
语句仅注册到单个事件,或者无论接收到哪个
事件,它都应以相同的方式运行,则可以省略该语句


EventRegister
已将
EventListener
(s)存储在映射中,这意味着每个侦听器将只获取其订阅的
事件类型。此外,
EventRegister
使用
Set
s,这意味着
EventListener
最多只能接收一次事件(以防止如果有人意外地将侦听器注册两次,侦听器将接收两个事件)

在反复讨论了几乎所有人的建议之后,我最终选择了一个标准侦听器接口和侦听器列表的略微修改路线。我从Swing的
EventListenerList
开始,但对几十种消息类型的添加/删除方法的数量感到失望。我意识到,在维护单个
EventListenerList
的同时,这无法压缩,因此我开始考虑为每种类型创建一个单独的列表。这使得它类似于.NET事件,其中每个事件都有自己的委托列表,在引发时激发。我想避免大量的添加/删除方法,所以我创建了一个快速的
事件
类,它看起来如下所示:

公共类事件{
私有列表侦听器=新的ArrayList();
公共void addListener(T listener){
添加(侦听器);
}
公共void removeListener(T listener){
删除(侦听器);
}
公共列表getListeners(){
返回听众;
}
}
然后我保留了这个类的几个实例,每个实例都是根据一个侦听器键入的,因此
事件
等等。然后我的类可以调用addm
interface GenericListener<T extends Event> {
   public void handle(T t);
}
Map<Class<T extends Event>, Collection<GenericListener<T extends Event>>>
Map<Class<T extends MyListener>, Collection<MyListener>>
public enum Event {
    A,
    B,
    C
}

public interface EventListener {
    void handle(Event event);
}

public class EventListenerImpl implements EventListener {
    @Override
    public void handle(Event event) {
        switch(event) {
            case A: 
                // ...
                break;
        }
    }
}

public class EventRegistry {
    private final Map<Event, Set<EventListener>> listenerMap;

    public EventRegistry() {
        listenerMap = new HashMap<Event, Set<EventListener>>();
        for (Event event : Event.values()) {
            listenerMap.put(event, new HashSet<EventListener>());
        }
    }

    public void registerEventListener(EventListener listener, Event event) {
        Set<EventListener> listeners = listenerMap.get(event);
        listeners.add(listener);
    }

    public void fire(Event event) {
        Set<EventListener> listeners = listenerMap.get(event);
        for (EventListener listener : listeners) {
            listener.handle(event);
        }
    }
}
  for (MessageTypeAListener listener : messageTypeAEvent.getListeners())
      listener.onMessageTypeA(value);