我的目标是在Kotlin中实现一个非常简单的事件总线,不使用任何第三方库。我通过以下代码实现了这个目标。
class EventListener<T>(
val owner: Any,
val event: Class<T>,
val callback: (T) -> Unit
)
interface IEventBus {
fun <T> subscribe(owner: Any, event: Class<T>, callback: (T) -> Unit)
fun unsubscribe(owner: Any)
fun <T> push(event: T)
}
class EventBus : IEventBus {
private val _listeners = mutableListOf<EventListener<*>>()
override fun <T> subscribe(owner: Any, event: Class<T>, callback: (T) -> Unit) {
val listener = EventListener(owner, event, callback)
_listeners.add(listener)
}
override fun unsubscribe(owner: Any) {
_listeners.removeAll {
it.owner == owner
}
}
override fun <T> push(event: T) {
_listeners.forEach { listener ->
try {
val l = listener as EventListener<T> // is always a success
l.callback(event) // throws an exception if can't handle the event
} catch (ex: Exception) { }
}
}
}
然后使用方式如下:
// register listener
bus.subscribe(this, String::class.java) {
print(it)
}
// push an event (from somewhere else in the project)
bus.push("Hello world!")
它能够正常工作,而且完全可用。但我对它不满意...将listener强制转换为EventListener总是会返回一些内容,如果l.callback(event)无法处理事件类型,它就会抛出异常。因此,如果有许多侦听器被订阅,它将产生许多不需要的异常,这些异常将被忽略。
我更喜欢首先进行某种检查,例如:
if (listener is EventListener<T>)
listener.callback(event)
但我发现JVM在编译后会丢失有关泛型类型的信息。我还发现,使用Kotlin的inline和reified可以绕过这个问题,但是对于来自接口的方法,这些方法无法使用...
所以我的问题是,您是否知道处理这种泛型问题的更优雅的方法?
Int
可以同时引用原始的 Javaint
和封装的 JavaInteger
。如果您想支持原始类型,有两种可能性:将Class<T>
替换为KClass<T>
或检查event
是否可分配给原始类型。例如event is Int
、event is Long
。如果可以的话,我建议您将Class
更改为KClass
,因为它将避免您对原始类型进行所有检查。 - Giorgio Antonioli