I have several types of events, for example:
abstract class Event {
static class KeyPress extends Event { ... }
static class KeyRelease extends Event { ... }
static class KeyHold extends Event { ... }
// ...
}
And many listeners which respond to some of the above events by registering them in an event handler. It looks like this currently:
abstract class AbstractListener {
Set<Class<? extends Event>> eventTypes;
protected abstract boolean respond(Event event);
}
class DownKeyListener exteneds AbstractListener {
DownKeyListener () {
// just prepares to register to receive these events. doesn't matter how exactly.
eventTypes.add(KeyPress.class);
eventTypes.add(KeyHold.class);
//no KeyRelease e.g.
}
boolean respond(Event event) {
if (event instanceof KeyPress)
return handleKeyPress(event);
else if (event instanceof KeyHold)
return handleKeyHold(event);
return false;
}
private boolean handleKeyPress(KeyPress e) { ... }
private boolean handleKeyHold(KeyHold e) { ... }
}
What I don't like about this is that there is nothing forcing or checking at least the relation between the registered events and the checks for them and handling in the respond method. This keeps leading to developer bugs. it's also a lot of code with instanceof
s for little benefit (I would say).
So I though about doing something "smart" like this: create a map between the event types and the handlers so each event that is registered for handling will have a handler:
abstract class AbstractListener {
Map<Class<? extends Event>, Function<? extends Event, Boolean> map = new ...
// ^ doesn't ensure these event ^: are the same but at least
// that someone responds
// to ensure same event i can do
protected <T extends Event> void register(Class<T> event, Function<T, Boolean> function) {
map.put(event, function);
}
protected abstract boolean respond(Event event);
}
And then:
class DownKeyListener exteneds AbstractListener {
DownKeyListener () {
Map.put(KeyPress.class, keyPressFunction);
Map.put(KeyHold.class, keyHoldFunction);
}
boolean respond(Event event) {
Function<? extends Event, Boolean> f = map.get(event.getClass());
return f == null ? false : f.apply(event);
}
Function<KeyPress, Boolean> keyPressFunction = event -> ...;
Function<KeyHold, Boolean> keyHoldFunction = event -> ...;
}
Of course this doesn't work because of generics. apply
gives an error and I understand why
The method apply(capture#4-of ? extends Event) in the type Function<capture#4-of ? extends Event,Boolean> is not applicable for the arguments (capture#5-of ? extends Event)
I don't know how to get what I want working correctly. Some things I had in mind:
- Cast the result of
map.get
to something that will ensure it works properly - Change the
respond
method to be genericprotected abstract <T extends Event> boolean respond(T event);
which give the similar error:
The method apply(capture#3-of ? extends Event) in the type Function<capture#3-of ? extends Event,Boolean> is not applicable for the arguments (T)
Anyone has a suggestion on achieving what I want in any way?
Event
class features the events. "no subscriptions" subscription is done after the event types are added in the constructor. How they are sent to the event manager isn't important unless you want more code but then you ask to simplify it. I don't understand what you want.class
keyword. This isn't Java code, it's some weird pseudo-code.