"supposedly" 正确类型的 Kotlin 类型推断
Kotlin type inference on "supposedly" right types
我是 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,那么它的类型必须是 invariant 或 covariant (未声明 [=18=])。如果它让你传递你的 EventListener<MyEvent>
就好像它是一个 EventListener<Event>
,那么你的 MyBus
class 可能会用一些 Event
调用 listener.handle(event)
那不是 MyEvent
,例如 AnotherEvent
。然后你会得到一个 ClassCastException
当它试图将这个 AnotherEvent
转换为 MyEvent
.
为了能够存储不同类型的不变EventHandlers,您将不得不使用星投影去除方差限制,并在从地图中检索它们时投射它们。因此,将映射键变成 class 对象而不仅仅是字符串。由于在使用星形投影类型时您不会得到编译器的帮助,因此您需要小心,您只是将一个项目添加到您的 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 {}
A class 实现接口:
class HelloEventListener: EventListener<HelloEvent> {
override fun handle(event: HelloEvent) {
event.sayHello()
}
}
现在您有一个可以处理 仅 HelloEvent
的 EventListener。尝试将其视为 EventListener<Event>
:
val eventListener: EventListener<Event> = HelloEventListener() // COMPILE ERROR!
假设编译器没有阻止你这样做而你这样做:
val eventListener: EventListener<Event> = HelloEventListener()
eventListener.handle(BoringEvent()) // CLASS CAST EXCEPTION AT RUN TIME!
如果允许这样做,您的 HelloEventListener 将尝试在没有该功能的 BoringEvent 上调用 sayHello()
,因此它会崩溃。这就是泛型在这里保护您免受的影响。
现在假设您的 HelloEventListener.handle()
没有调用 event.sayHello()
。那么,它就可以安全地处理 BoringEvent。但是编译器并没有为您进行那种级别的分析。它只知道您声明的内容,HelloEventListener 无法处理除 HelloEvent 之外的任何内容。
我是 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,那么它的类型必须是 invariant 或 covariant (未声明 [=18=])。如果它让你传递你的 EventListener<MyEvent>
就好像它是一个 EventListener<Event>
,那么你的 MyBus
class 可能会用一些 Event
调用 listener.handle(event)
那不是 MyEvent
,例如 AnotherEvent
。然后你会得到一个 ClassCastException
当它试图将这个 AnotherEvent
转换为 MyEvent
.
为了能够存储不同类型的不变EventHandlers,您将不得不使用星投影去除方差限制,并在从地图中检索它们时投射它们。因此,将映射键变成 class 对象而不仅仅是字符串。由于在使用星形投影类型时您不会得到编译器的帮助,因此您需要小心,您只是将一个项目添加到您的 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 {}
A class 实现接口:
class HelloEventListener: EventListener<HelloEvent> {
override fun handle(event: HelloEvent) {
event.sayHello()
}
}
现在您有一个可以处理 仅 HelloEvent
的 EventListener。尝试将其视为 EventListener<Event>
:
val eventListener: EventListener<Event> = HelloEventListener() // COMPILE ERROR!
假设编译器没有阻止你这样做而你这样做:
val eventListener: EventListener<Event> = HelloEventListener()
eventListener.handle(BoringEvent()) // CLASS CAST EXCEPTION AT RUN TIME!
如果允许这样做,您的 HelloEventListener 将尝试在没有该功能的 BoringEvent 上调用 sayHello()
,因此它会崩溃。这就是泛型在这里保护您免受的影响。
现在假设您的 HelloEventListener.handle()
没有调用 event.sayHello()
。那么,它就可以安全地处理 BoringEvent。但是编译器并没有为您进行那种级别的分析。它只知道您声明的内容,HelloEventListener 无法处理除 HelloEvent 之外的任何内容。