Ok so the code below is an event system that does the following:
The code is as follows:
class EventHandler {
companion object {
val handlers = HashMap<KClass<out Event>, MutableSet<Int>>()
val idMap = HashMap<Int, (Event) -> Unit>();
/**
* @param event Class of the event you are registering
* @param handler What to do when the event is called
*/
fun <T : Event> register(event: KClass<T>, handler: (T) -> Unit): Int {
var id: Int = 0;
while(idMap[id] != null) {
id++;
}
var list = handlers.getOrPut(event, {mutableSetOf()});
list.add(id);
idMap[id] = handler;
return id;
}
}
}
The intended use of this method would be something like this:
EventHandler.register(ChatEvent::class) { onChat ->
println(onChat.message)
}
There is an error at the following line: idMap[id] = handler;
The error is because the handler is of type (T) -> Unit
, although it needs to be (Event) -> Unit
in order to add it to the idMap
. Although I said that T
should extend Event
when I created it, so this shouldn't be a problem. Does anyone know why this happens of if there is a solution?
The problem comes from the fact that idMap
takes a function that receives an Event
- any kind of Event
.
Then you try to register a function that takes a specified subclass of Event
, which, when you pull it back out, the compiler won't be able to tell what possible subclass it receives. Yes, you're storing the specific type in your other map, but the compiler can't use that.
I do not believe you can create the mapping system that you want to create. Not without a few more layers of indirection or abstraction...
The reason you get this error is explained well by @jacob-zimmerman in his answer: (T) -> Unit
is not a subtype of (Event) -> Unit
(on the contrary it is a supertype).
You could make an unchecked downcast to required function type:
idMap[id] = handler as (Event) -> Unit
But then before invoking such handler you must check that an event has type that the handler could accept, for example by querying handler from map based on the type of the event:
fun invoke(event: Event) {
val kclass = event.javaClass.kotlin
val eventHandlers = handlers[kclass]?.map { idMap[it]!! } ?: return
eventHandlers.forEach { it.invoke(event) }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With