Reputation: 2748
I'm writing an event system for a game. Here's an example of the API for subscribing to a channel.
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. I'd been getting a warning from my IDE about an unchecked call in the publish method. 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. How can I ensure that event
is of the right type for the handler
(in the excerpt below)?
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
}
}
}
public abstract class EventHandler<T extends GameEvent> {
public abstract void handle(T event);
}
Upvotes: 2
Views: 371
Reputation: 4602
I suppose the problem is because your loop uses an untyped EventHandler, while it calls a generic method.
I would use the list as
List<EventHandler<? extends GameEvent>>
Same for the loop variable in the for statement.
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. If that is too slow, build a Map with the GameEvent.class as the key and a list of interrested handlers as value. 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.
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.
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.
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 ;-)
Upvotes: 1
Reputation: 1936
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.
Upvotes: 2