我正在尝试使用按类编制索引的事件来构建事件处理程序。您将进行注册以侦听InterestingEvent : Event
类的事件,例如,每当有新的InterestingEvent()
发送给经理时,您都会收到通知。
我无法让Kotlin通用代码正常工作。这是到目前为止我能做的最好的事情:
class EventManager{
private val listeners: MutableMap<KClass<out Event>, MutableList<EventListener<in Event>>> = HashMap()
fun <T : Event> register(event: KClass<out T>, listener: EventListener<T>) {
val eventListeners: MutableList<EventListener<T>> = listeners.getOrPut(event) { ArrayList() }
eventListeners.add(listener)
}
fun notify(event: Event) {
listeners[event::class]?.forEach { it.handle(event) }
}
}
我的getOrPut
通话想要一个MutableList<EventListener<T>>
,但却找到了MutableList<EventListener<in Event>>
。
理想情况下,我也将摆脱register
的KClass参数,但我认为这是不可能的。
可以在这里做我想做的事吗?
答案 0 :(得分:2)
这有点困难,因为您想在同一地图内的列表中存储EventListener<T: Event>
个项目的混合,因此对于任何给定列表来说T
都是未知的。然后,您想像使用T
一样使用列表中的项目,以便可以将其传递给notify()
。因此,这会引起麻烦。许多库只是通知通用事件,而接收者则进行转换工作。但是您可以用几种方法来将负担加到通知库中,因为它知道在逻辑上可以保证映射键和列表项类型之间的关系。
这是一些经过调整的代码,具有两种形式的通知。
class EventManager {
val listeners: MutableMap<KClass<*>, MutableList<EventListener<out Event>>> = mutableMapOf()
inline fun <reified T : Event> register(listener: EventListener<T>) {
val eventClass = T::class
val eventListeners: MutableList<EventListener<out Event>> = listeners.getOrPut(eventClass) { mutableListOf() }
eventListeners.add(listener)
}
inline fun <reified T: Event> notify(event: T) {
// here we have an unsafe action of going from unknown EventListener<T: Event> to EventListener<R>
// which we know it is because of our code logic, but the compiler cannot know this for sure
listeners[event::class]?.asSequence()
?.filterIsInstance<EventListener<T>>() // or cast each item in a map() call
?.forEach { it.handle(event) }
}
// or if you don't know the event type, this is also very unsafe
fun notifyUnknown(event: Event) {
listeners[event::class]?.asSequence()
?.filterIsInstance<EventListener<Event>>()
?.forEach { it.handle(event) }
}
}
给出一些简单的示例类:
open class Event(val id: String)
class DogEvent : Event("dog")
class CatEvent: Event("cat")
interface EventListener<T: Event> {
fun handle(event: T): Unit
}
以下测试有效:
fun main() {
val mgr = EventManager()
mgr.register(object : EventListener<DogEvent> {
override fun handle(event: DogEvent) {
println("dog ${event.id}")
}
})
mgr.register(object : EventListener<CatEvent> {
override fun handle(event: CatEvent) {
println("cat ${event.id}")
}
})
mgr.notify(Event("nothing")) // prints: <nothing prints>
mgr.notify(CatEvent()) // prints: cat cat
mgr.notify(DogEvent()) // prints: dog dog
mgr.notifyUnknown(CatEvent()) // prints: cat cat
mgr.notifyUnknown(DogEvent()) // prints: dog dog
}
这很有效,因为它是一个独立的系统,其中我们对类型安全性的完全无视并没有真正暴露出危险的方式,因此是“足够安全的”。如果我想一种更好的方法,我将更新帖子。一种方法是创建一个新的“类到列表”列表,该列表具有关联的合同,该合同试图将值类型与键类型联系起来,并有助于消除强制转换。
答案 1 :(得分:0)
Pavlov Liapota 也用Kotlin的正式闲暇回答了我的问题。这是他的解决方案:
class EventManager{
private val listeners: MutableMap<KClass<out Event>, MutableList<EventListener<Event>>> = HashMap()
inline fun <reified T : Event> register(listener: EventListener<T>) {
register(T::class, listener)
}
fun <T : Event> register(eventClass: KClass<out T>, listener: EventListener<T>) {
val eventListeners = listeners.getOrPut(eventClass) { ArrayList() }
eventListeners.add(listener as EventListener<Event>)
}
fun notify(event: Event) {
listeners[event::class]?.forEach { it.handle(event) }
}
}