简体   繁体   English

Java泛型事件系统未检查的调用

[英]Java generics event system unchecked call

I'm writing an event system for a game. 我正在为游戏编写事件系统。 Here's an example of the API for subscribing to a channel. 这是订阅频道的API示例。

Events.subscribe(Channel.COLLISION, new EventHandler<CollisionEvent>() {
    @Override
    public void handle(CollisionEvent event) {
    }
});

and publishing 和出版

Events.publish(Channel.COLLISION, new CollisionEvent(data));

My problem arises when passing objects to the publish method. 将对象传递到publish方法时出现我的问题。 I'd been getting a warning from my IDE about an unchecked call in the publish method. 我已经从IDE中收到有关publish方法中未经检查的调用的警告。 I'm trying to find a way to check to make sure the type of the object is compatible with the EventHandler 's generic type. 我试图找到一种方法来检查对象的类型是否与EventHandler的泛型类型兼容。 How can I ensure that event is of the right type for the handler (in the excerpt below)? 如何确保eventhandler的正确类型(在下面的摘录中)?

Events.java Events.java

private static Map<Channel, ArrayList<EventHandler>> eventHandlers
        = new HashMap<Channel, ArrayList<EventHandler>>();

public static void publish(EventType eventType, GameEvent event) {
    ArrayList<EventHandler> handlers = eventHandlers.get(eventType);
    if(handlers != null) {
        for (EventHandler handler : handlers) {
            handler.handle(event); // unchecked call warning occurs here
        }
    }
}

EventHandler.java EventHandler.java

public abstract class EventHandler<T extends GameEvent> {
    public abstract void handle(T event);
}

You should be getting this warning - you could potentially be passing in a different event type. 应该收到此警告-您可能传递了其他事件类型。 You may need to make a decision on how you want to architect your solution - as is, you could change it so the handle function on your EventHandler objects can take in any type of event, not just the one the EventHandler is parameterized for, and then have the EventHandler check whether the event it's asked to handle is the type it is interested in. Alternatively, you can move that type of logic to the event publisher - whenever an event comes it, the for loop can check if the event handler it's currently on supports that type of event, and if so, tell it to handle it. 您可能需要决定如何构造解决方案-可以更改它,以便EventHandler对象上的handle函数可以接受任何类型的事件,而不仅仅是EventHandler为其参数化的EventHandler ,并且然后让EventHandler检查请求处理的事件是否是它感兴趣的类型。或者,您可以将该逻辑类型移动到事件发布者-每当有事件发生时,for循环都可以检查它是否是事件处理程序当前处于启用状态的事件,如果是,请告诉它进行处理。

I suppose the problem is because your loop uses an untyped EventHandler, while it calls a generic method. 我想问题是因为您的循环使用了无类型的EventHandler,而它却调用了通用方法。

I would use the list as 我将使用列表作为

List<EventHandler<? extends GameEvent>>

Same for the loop variable in the for statement. 与for语句中的循环变量相同。

Thomas 托马斯

Update: Sorry, you look for the type of event, thought you don't want the warning. 更新:对不起,您在不想得到警告的情况下寻找事件的类型。

How about having the EventHandler decide, if that type is interesting inside the handle method. 如果在handle方法中该类型是否有趣,如何让EventHandler决定。 If that is too slow, build a Map with the GameEvent.class as the key and a list of interrested handlers as value. 如果太慢,请使用GameEvent.class作为键,并使用间隔处理程序列表作为值来构建Map。 The handler gives the list of events at subscription time. 处理程序在订阅时提供事件列表。

To be more precise: Your current way to subscribe to an event seems to suggest, that the handler has the proper method implemented. 更准确地说:您当前的事件订阅方式似乎暗示处理程序已实现了正确的方法。 Yet, your subscribe method does not enforce a relationship between handler and subscribed event. 但是,您的subscribe方法不会在处理程序和订阅事件之间强制执行关系。

If you modify the subscription to bind the handler not to the Event-Channel, but to the event itself, the compiler will do it's very best to avoid any handler not reacting to the right event getting access to the channel: 如果您修改订阅以将处理程序不是绑定到事件通道,而是绑定到事件本身,则编译器将做到最好,避免任何处理程序对正确的事件不做出反应,以访问该通道:

public <T extends GameEvent> void subscribe(Class<T> clazz, EventHandler<T> handler)

Inside the subscribe implementation, you place the handler in a list, that you keep in a map, keyed with clazz. subscribe实现内部,将处理程序放置在列表中,并保留在地图中,并用clazz键输入。

if( !map.containsKey(clazz) ) {
    map.put(clazz, new ArrayList<EventHandler<T>>());
}

// add the handler
map.get(clazz).add(handler);

The publish method will use the given concrete GameEvent.class as key to obtain the right list from the map and call all the handlers. publish方法将使用给定的具体GameEvent.class作为键来从地图获取正确的列表并调用所有处理程序。

By regulating the access during subscription, you don't need to pay attention during call. 通过在订阅过程中调节访问权限,您在通话过程中无需关注。 If someone uses dirty tricks to get into the wrong list, he deserves to crash, as long as you don't write code for atomic bombs ;-) 如果有人使用肮脏的把戏弄错了名单,只要您不为原子弹编写代码,他就应该崩溃;-)

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

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