需要使用Java泛型的帮助吗

需要使用Java泛型的帮助吗,java,generics,Java,Generics,我试图创建一个系统来响应应用程序中发生的事件,类似于观察者模式。在我的系统中,EventProducers触发事件和EventConsumers响应这些事件,两者通过一个中央集线器连接: 目前,我将忽略EventProducer,重点关注EventHub和EventConsumer: interface EventConsumer<E extends Event> { void respondToEvent(E event); } class EventHub {

我试图创建一个系统来响应应用程序中发生的事件,类似于观察者模式。在我的系统中,
EventProducer
s触发事件和
EventConsumer
s响应这些事件,两者通过一个中央集线器连接:

目前,我将忽略
EventProducer
,重点关注
EventHub
EventConsumer

interface EventConsumer<E extends Event> {
    void respondToEvent(E event);
}

class EventHub {
    private HashMap<Class</*event type*/>, HashSet<EventConsumer</*event type*/>>> subscriptions;
    public <E extends Event> void fireEvent(E event) {
        /* For every consumer in the set corresponding to the event type {
            consumer.respondToEvent(event);
        } */
    }
    public <E extends Event> void subscribeToEvent(EventConsumer<E> consumer) {
        /* Insert consumer into the set corresponding to E */
    }
}
因此,
EventConsumer
由与
相同的类型参数化,但我能得到的最接近的类型是

HashMap<Class<? extends Event>, HashSet<EventConsumer<? extends Event>>>
但是我不能在运行时获得E类


如何解决这些问题?我走错方向了吗?

至于地图,我将其保留如下:

HashMap<Class<? extends Event>, Set<EventConsumer<? extends Event>>> subscriptions;

HashMap为什么不将EventHub类参数化?有挑战吗

interface EventConsumer<E extends Event> {
void respondToEvent(E event);
}

class EventHub<E extends Event> {
 private HashMap<Class<E>, HashSet<EventConsumer<E>>> subscriptions;

 public void fireEvent(E event) {
/*
 * For every consumer in the set corresponding to the event type {
 * consumer.respondToEvent(event); }
 */
  }

  public void subscribeToEvent(EventConsumer<E> consumer) {
  /* Insert consumer into the set corresponding to E */
}
}
接口事件消费者{
无效响应事件(E事件);
}
类事件中心{
私有HashMap订阅;
公共火灾事件(E事件){
/*
*对于与事件类型对应的集合中的每个使用者{
*consumer.respondToEvent(事件);}
*/
}
公共无效subscribeToEvent(事件消费者){
/*将使用者插入到与E对应的集合中*/
}
}
然后在所有函数签名中使用E

编辑1: 好吧,既然我更清楚地理解了你的问题,那么我们开始:


EventConsumer类可以保留它支持/处理的EventType。EventType是一个枚举。在地图中,根据EventType存储消费者

您可以做的是用它自己的参数化类包装映射。将参数指定给类-您可以在映射中使用它。诸如此类:

public class EventsMap<E extends Event> {
    HashMap<Class<E>, HashSet<E>> map;
}
公共类事件映射{
HashMap图;
}

至于订阅,我将使用ty1824的答案。

好的,从结尾开始:

首先,您实际上可以在运行时获取泛型类型参数类型。只是你只能在特殊情况下才能这么做:

static class A<E extends EventObject> {
}

static class B extends A<MouseEvent> {
}

public static void main(String[] args) {
    System.out.println(B.class.getGenericSuperclass());
}
静态A类{
}
静态类B扩展了{
}
公共静态void main(字符串[]args){
System.out.println(B.class.getGenericSuperclass());
}
注意
B
是非泛型的,但从泛型父级继承

如果超类是参数化类型,则
类型
返回的对象必须准确反映实际类型 源代码中使用的参数

你是否能把它用于任何用途是另一个问题。我没有试过。我看到的大多数代码都显式地传递了一个
实例作为解决方法


其次,这里的
Map
不能(据我所知)用泛型来修复。当然,您可以实现类型安全映射,但我认为还有一件更有趣的事情需要考虑:当您触发事件时,您可能希望将其发送给订阅此特定事件类的所有人以及订阅其父类的所有人。因此,仅仅
event.getClass()
就不够了。

您可以从
EventConsumer
类中删除泛型。但是您必须在
EventConsumer
的每个实现中强制转换事件对象

interface EventConsumer {
    void respondToEvent(Event event);
}

class ClickEventConsumer implements EventConsumer {
   public void respondToEvent(Event event){
     ClickEvent ce = (ClickEvent)event;
     //...
   }
}

class EventHub {
  private HashMap<Class<? extends Event>, HashSet<EventConsumer>> subscriptions;

  public void fireEvent(Event event) {
    HashSet<EventConsumer> consumers = subscriptions.get(event.getClass());
    if (consumers != null){
      for (EventConsumer ec : consumers){
        ec.respondToEvent(event);
      }
    }
  }

  public void subscribeToEvent(Class<? extends Event> clazz, EventConsumer consumer) {
    HashSet<EventConsumer> consumers = subscriptions.get(clazz);
    if (consumers == null){
      consumers = new HashSet<EventConsumer>();
      subscriptions.put(clazz, consumers);
    }
    consumers.add(consumer);
  }
}
接口事件消费者{
无效响应事件(事件);
}
类ClickEventConsumer实现EventConsumer{
公共无效响应事件(事件){
ClickEvent ce=(ClickEvent)事件;
//...
}
}
类事件中心{

私有hashmap这不是一个选项,因为
EventHub
需要处理多种类型的
Event
。如果我使用
neweventhub
,那么
EventHub
将只对
KeyPressEvent
工作,而不是例如
MouseClickEvent
您的EventConsumer类可以保留一个事件类型它支持/处理。EventType是一个枚举。在您的映射中,您根据EventType存储使用者。是否有任何原因我希望将其包装而不是将其子类包装:
类EventsMap扩展HashMap
@Austin是的,有:1.您只会将实现使用的方法公开,以提供更好的封装2。这将允许您在不对原始HashMap 3进行单元测试的情况下对实现进行单元测试。您将坚持优先组合而非继承原则@Gandalf这是一个有趣的概念,我还没有想到。我需要对此进行更多研究,但我认为它实际上甚至可以应用于此应用程序的更多方面,而不是wha这是个问题……非常感谢!@krakover Wait,这不是限制了
EventsMap
只映射单一类型的事件吗?因为如果
E
是例如
ClickEvent
,那么每个键都是
Class
,这显然不是很有用…@Austin FCoI让测试和首先的单元测试变得更加容易大多数情况下都是y。但就像大多数编程原则一样,如果在特殊情况下有意义的话,你必须平衡各种原因。实际上,我喜欢这种方法。最初,我希望能够做一些类似于
类的事情,比如SomeConsumer实现EventConsumer,EventConsumer
,但显然你做不到(我想这是有道理的)。有没有办法在消费者身上获得某种形式的
Event
多态性,以避免在
respondToEvent
中切换?比如
void respondToEvent(点击事件);void respondToEvent(按键事件)我不知道。我在玩这个游戏,但是我找不到一个好方法来做它,而不是在代码< Evthuub中创建所有的泛型混淆。你也应该考虑代码的可读性。即使它做得有点低效,如果它随着时间的推移更容易维护,我认为它是值得的。(除非你写的是电脑游戏、搜索引擎或其他每毫秒都很重要的东西)。对我来说,所有这些泛型使代码更难阅读和理解。
public class EventsMap<E extends Event> {
    HashMap<Class<E>, HashSet<E>> map;
}
static class A<E extends EventObject> {
}

static class B extends A<MouseEvent> {
}

public static void main(String[] args) {
    System.out.println(B.class.getGenericSuperclass());
}
interface EventConsumer {
    void respondToEvent(Event event);
}

class ClickEventConsumer implements EventConsumer {
   public void respondToEvent(Event event){
     ClickEvent ce = (ClickEvent)event;
     //...
   }
}

class EventHub {
  private HashMap<Class<? extends Event>, HashSet<EventConsumer>> subscriptions;

  public void fireEvent(Event event) {
    HashSet<EventConsumer> consumers = subscriptions.get(event.getClass());
    if (consumers != null){
      for (EventConsumer ec : consumers){
        ec.respondToEvent(event);
      }
    }
  }

  public void subscribeToEvent(Class<? extends Event> clazz, EventConsumer consumer) {
    HashSet<EventConsumer> consumers = subscriptions.get(clazz);
    if (consumers == null){
      consumers = new HashSet<EventConsumer>();
      subscriptions.put(clazz, consumers);
    }
    consumers.add(consumer);
  }
}