gpt4 book ai didi

generics - Kotlin 中具有泛型的优雅事件处理程序

转载 作者:行者123 更新时间:2023-12-03 08:57:50 25 4
gpt4 key购买 nike

我正在尝试构建一个事件处理程序,其中的事件按类索引。您可以注册监听 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>>相反。

理想情况下,我会将 KClass 参数删除为 register也是,但我认为这是不可能的。

可以做我在这里尝试的事情吗?

最佳答案

这有点困难,因为您想要存储 EventListener<T: Event> 的混合。同一 map 内列表中的项目,因此 T对于任何给定的列表都是未知的。然后您想要使用列表中的项目,就像 T 一样已知,因此可以将其传递给 notify() 。所以这会带来麻烦。许多库只是通知通用事件,而接收者则执行转换工作。但是您可以通过几种方式来将这种负担转移到您的通知库中,因为它知道从逻辑上讲, map 的键和列表项类型之间的关系是有保证的。

这里是一些调整后的代码,具有两种形式的通知。

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
}

这很有效,因为它是一个独立的系统,我们对类型安全的完全忽视并没有真正以危险的方式暴露出来,因此“足够安全”。如果我想到更好的方法,我会更新帖子。一种方法是创建一个新的类到列表的映射,该映射具有关联的契约,尝试将值类型与键类型连接起来并帮助消除强制转换。

关于generics - Kotlin 中具有泛型的优雅事件处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53661981/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com