简体   繁体   English

需要一些使用Java Generics的帮助

[英]Need some help using Java Generics

I am trying to make a system for responding to events that happen in my application, similar to the Observer pattern. 我正在尝试建立一个系统来响应我的应用程序中发生的事件,类似于Observer模式。 In my system, EventProducer s trigger events and EventConsumer s respond to those events, and the two are connected through a central hub: 在我的系统中, EventProducer触发事件, EventConsumer响应这些事件,两者通过中央集线器连接:

For the moment, I'm going to ignore EventProducer and focus on EventHub and EventConsumer : 目前,我将忽略EventProducer并专注于EventHubEventConsumer

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 */
    }
}

The problem lies in the declaration of the HashMap : I want to be able to do something like 问题在于HashMap的声明:我希望能够做类似的事情

HashMap<Class<E extends Event>, HashSet<EventConsumer<E>>>
// or
<E extends Event> HashMap<Class<E>, HashSet<EventConsumer<E>>>

So that the EventConsumer is parameterized by the same type the Class is, but the closest I can get is 因此EventConsumer的参数化类型与Class相同,但我能得到的最接近的是

HashMap<Class<? extends Event>, HashSet<EventConsumer<? extends Event>>>

But then this would allow things like a HashSet<EventConsumer<MouseClickEvent>> being assigned to Class<KeyPressEvent> , assuming both KeyPressEvent and MouseClickEvent subclass Event . 但是这样就可以将HashSet<EventConsumer<MouseClickEvent>>分配给Class<KeyPressEvent> ,假设KeyPressEventMouseClickEvent子类Event

A second problem is in subscribeToEvent : I need to be able to store the consumer in the correct set corresponding to its event, like in 第二个问题在于subscribeToEvent :我需要能够将消费者存储在与其事件相对应的正确集合中,例如

subscriptions.get(E.class).put(consumer)

but I cannot get the class of E at run-time. 但我无法在运行时获得E级。

How can I solve these problems? 我该如何解决这些问题? Am I going about this the wrong way? 我是以错误的方式来做这件事的吗?

What you can do is to wrap the Map with it's own parameterized class. 你可以做的是用它自己的参数化类包装Map。 given the parameter to the class - you can use it in the map. 给出类的参数 - 您可以在地图中使用它。 something like that: 类似的东西:

public class EventsMap<E extends Event> {
    HashMap<Class<E>, HashSet<E>> map;
}

As for subscribing - I'll use ty1824's answer.. 至于订阅 - 我将使用ty1824的答案..

As for the Map, I'd leave it as follows: 至于地图,我会留下如下:

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

And then use parameterized accessor methods like: 然后使用参数化的访问器方法,如:

<E extends Event> void addSubscription(Class<E> eventClass, EventConsumer<? super E> eventConsumer)

<E extends Event> Set<EventConsumer<? super E>> getSubscriptions(Class<E> eventClass)

As you already pointed out you cannot obtain the event class at runtime, so you'll need to have it provided by your API users as for example with the method signature of addSubscription provided above. 正如您已经指出的那样,您无法在运行时获取事件类,因此您需要由API用户提供它,例如上面提供的addSubscription方法签名。

You could remove the generics from the EventConsumer class. 您可以从EventConsumer类中删除泛型。 But you'd have to cast the Event object in each implementation of EventConsumer . 但是你必须在EventConsumer每个实现中EventConsumer Event对象。

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);
  }
}

Why don't you parameterise your EventHub class? 为什么不参数化你的EventHub类? any challanges? 任何挑战?

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 */
}
}

And then use E in all your function signatures. 然后在所有功能签名中使用E.

EDIT 1: Okay, since i understand your question more clearly, here we go: 编辑1:好的,因为我更清楚地理解你的问题,我们走了:

your EventConsumer Class can keep an EventType(s) that it supports/handles. 您的EventConsumer类可以保留它支持/处理的EventType。 EvenType is an Enum. EvenType是一个枚举。 In your Map you store Consumers against a EventType. 在Map中,您可以根据EventType存储使用者。

Well, starting from the end: 好吧,从结尾开始:

First, you can actually obtain generic type parameter type at runtime. 首先,您可以在运行时实际获取泛型类型参数类型。 It's just that you can only do it in a special case: 只是你只能在特殊情况下这样做:

static class A<E extends EventObject> {
}

static class B extends A<MouseEvent> {
}

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

Note B is non-generic, but is inherited from a generic parent. 注意B是非泛型的,但是继承自通用父级。

If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code. 如果超类是参数化类型,则返回的Type对象必须准确反映源代码中使用的实际类型参数。

Whether you can put it in any use is a different question. 您是否可以将它用于任何用途是一个不同的问题。 I didn't try. 我没试过。 Most of the code I saw explicitly pass a Class instance as a workaround. 我看到的大多数代码都明确地将Class实例作为变通方法传递。

Second, the Map here cannot (well, as far as I know) be fixed with generics. 其次,这里的Map不能(据我所知)用泛型修复。 You can implement a typesafe map of course, but I think there's one more interesting thing to consider: when you fire an event, you probably want to send it to all who subscribed to this particular event class and all who subscribed to its parents. 你当然可以实现一个类型安全的地图,但我认为还有一个有趣的事情要考虑:当你发起一个事件时,你可能想把它发送给订阅这个特定事件类的所有人以及所有订阅它的父母的人。 So just event.getClass() would be just a little bit less than enough. 所以只需要event.getClass()就可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM