好的,所以下面的代码是一个执行以下操作的事件系统:
代码如下:
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;
}
}
}
此方法的预期用途如下:
EventHandler.register(ChatEvent::class) { onChat ->
println(onChat.message)
}
以下行存在错误:idMap[id] = handler;
错误是因为处理程序属于(T) -> Unit
类型,但需要(Event) -> Unit
才能将其添加到idMap
。虽然我说T
在我创建Event
时会延长f n = n : f (n+1)
,所以这不应该是个问题。如果有解决方案,有谁知道为什么会发生这种情况?
答案 0 :(得分:4)
问题来自idMap
采用接收Event
- 任何类Event
的函数的事实。
然后你尝试注册一个带有Event
的指定子类的函数,当你把它拉出来时,编译器就不能告诉它接收到什么可能的子类。是的,您在其他地图中存储了特定类型,但编译器无法使用该类型。
我不相信你可以创建你想要创建的地图系统。不是没有几层间接或抽象......
答案 1 :(得分:3)
@ jacob-zimmerman在answer中解释了出现此错误的原因:(T) -> Unit
不是(Event) -> Unit
的子类型(相反,它是超类型)。
您可以将未经检查的向下转换为所需的函数类型:
idMap[id] = handler as (Event) -> Unit
但是在调用这样的处理程序之前,你必须检查事件是否具有处理程序可以接受的类型,例如通过根据事件的类型从map查询处理程序:
fun invoke(event: Event) {
val kclass = event.javaClass.kotlin
val eventHandlers = handlers[kclass]?.map { idMap[it]!! } ?: return
eventHandlers.forEach { it.invoke(event) }
}
答案 2 :(得分:1)
我想出了一个实现:
class Handlers<T: Event> {
val backingList = ArrayList<(T) -> Unit>()
fun add(handler: (T) -> Unit) {
backingList.add(handler)
}
fun remove(handler: (T) -> Unit) {
backingList.remove(handler)
}
fun handleEvent(event: T) {
backingList.forEach { handle ->
handle(event)
}
}
}
class HandlerMap {
val handlerMap = HashMap<KClass<out Event>, Handlers<out Event>>()
fun <T : Event> register(event: KClass<T>, handler: (T) -> Unit) {
val list: Handlers<T>
if(!handlerMap.containsKey(event)) {
list = Handlers<T>()
handlerMap.put(event, list)
}
else {
list = handlerMap.get(event) as Handlers<T>
}
list.add(handler)
}
fun <T: Event> getHandlers(event: KClass<T>): Handlers<T> {
return handlerMap.get(event) as Handlers<T>
}
}
由于Map是开放式的,它必须进行一些转换,但是系统是关闭的,因此Handlers
对象保证是完全正确的类型。
由于实施的变化,我很确定你并不关心索引/&#34; id&#34;我已经尝试了它的实现,它很好;如果你想要的话,没有真正的麻烦。