gpt4 book ai didi

Kotlin 对 "supposedly"正确类型的类型推断

转载 作者:行者123 更新时间:2023-12-02 13:06:58 26 4
gpt4 key购买 nike

我是 Kotlin 的新手,我正在使用它。我非常想创建一个非常基本的事件总线。所以我想出了这个

interface Event
interface EventListener<E : Event> {
fun handle(event: E)
}

interface EventBus {
fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>)
}

class MyBus() : EventBus {
private val eventListeners: MutableMap<String, MutableList<EventListener<out Event>>> = mutableMapOf()

constructor(listeners: List<Pair<Class<Event>, EventListener<Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}

override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val key = aClass.name
val listeners: MutableList<EventListener<out Event>> = eventListeners.getOrPut(key) { mutableListOf() }
listeners.add(eventListener)
}
}


val bus = MyBus(
listOf(
MyEvent::class.java to MyEventListener()
)
)

class MyEvent : Event
class AnotherEvent : Event
class MyEventListener : EventListener<MyEvent> {
override fun handle(event: MyEvent) {
}
}

发生的情况是,当我尝试使用接受对列表的构造函数创建 MyBus 时,我得到
Type inference failed. Expected type mismatch: inferred type is List<Pair<Class<MyEvent>,MyEventListener>> but List<Pair<Class<Event>,EventListener<Event>>> was expected
但是,如果我将构造函数更改为类似
constructor(listeners: List<Pair<Class<out Event>, EventListener<out Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}

几乎到处添加,然后 MyBus 构造函数起作用,但是对 registerListener(..) 的调用由于与以前相同的确切原因而中断。所以解决这个问题的唯一方法是在 registerListener 函数上添加“out”。
我怀疑我在这里做错了什么,但我不知道究竟是什么。有什么帮助吗?

最佳答案

如果你想要你的 EventListener能够消费Event s,那么它的类型必须是不变的或协变的(未声明 out )。如果它让你通过你的EventListener<MyEvent>好像它是一个 EventListener<Event> , 然后你的 MyBus类(class)可能会调用 listener.handle(event)上面有一些Event那不是 MyEvent ,如 AnotherEvent .然后你会得到一个ClassCastException当它试图转换这个 AnotherEventMyEvent .
为了能够存储不同类型的不变事件处理程序,您必须通过使用星形投影来消除方差限制,并在您从 map 中检索它们时进行转换。因此,将映射键变成类对象,而不仅仅是字符串。由于在使用星型投影类型时您不会得到编译器的帮助,因此您需要小心,您只是向 MutableMap 添加一个与与之关联的 Class 键类型相同的项。然后,当您检索项目时,仅转换为不变类型。
您问题的另一部分是您的构造函数需要一个泛型类型。现在它只适用于 Event所以它不能处理 Event 的子类型. Kotlin(还没有?)不支持构造函数的泛型类型,因此您必须使用工厂函数来执行此操作。
这是上述所有内容的示例。

class MyBus() : EventBus {
private val eventListeners: MutableMap<Class<*>, MutableList<EventListener<*>>> = mutableMapOf()

override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val listeners = retrieveListeners(aClass)
listeners.add(eventListener)
}

private fun <E: Event> retrieveListeners(aClass: Class<E>): MutableList<EventListener<E>> {
@Suppress("UNCHECKED_CAST")
return eventListeners.getOrPut(aClass) { mutableListOf() } as MutableList<EventListener<E>>
}
}

// Factory function
fun <E : Event> myBusOf(listeners: List<Pair<Class<E>, EventListener<E>>>): MyBus {
return MyBus().apply {
listeners.forEach {
registerListener(it.first, it.second)
}
}
}
您可能希望从 <List>Pair 更改工厂参数的类型。到 vararg Pair所以更容易使用。

这是一个简化的示例来解释方差限制。
您的事件消费者界面:
interface EventListener<E : Event> {
fun handle(event: E)
}
Event 的两种实现:
class HelloEvent: Event {
fun sayHello() = println("Hello world")
}

class BoringEvent: Event {}
实现接口(interface)的类:
class HelloEventListener: EventListener<HelloEvent> {
override fun handle(event: HelloEvent) {
event.sayHello()
}
}
现在你有了一个可以处理 的 EventListener仅限 HelloEvent s。尝试将其视为 EventListener<Event> :
val eventListener: EventListener<Event> = HelloEventListener() // COMPILE ERROR!
想象一下编译器没有阻止你这样做,你这样做:
val eventListener: EventListener<Event> = HelloEventListener()
eventListener.handle(BoringEvent()) // CLASS CAST EXCEPTION AT RUN TIME!
如果允许,您的 HelloEventListener 将尝试调用 sayHello()在没有那个功能的 BoringEvent 上,所以它会崩溃。这就是泛型在这里保护您免受伤害的原因。
现在假设您的 HelloEventListener.handle()未调用 event.sayHello() .那么,它可以安全地处理一个 BoringEvent。但是编译器并没有为您进行这种级别的分析。它只知道你声明了什么,HelloEventListener 不能处理除了 HelloEvent 之外的任何东西。

关于Kotlin 对 "supposedly"正确类型的类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62881828/

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