My goal is to implement very simple event bus in Kotlin without any 3rd party libraries. I accomplished that with the code below.
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) { }
}
}
}
And then the usage would be like this:
// register listener
bus.subscribe(this, String::class.java) {
print(it)
}
// push an event (from somewhere else in the project)
bus.push("Hello world!")
It works and is completely usable, however I am not satisfied with it... Casting listener as EventListener will always return something and then if l.callback(event) can't handle the event type it will throw an exception. So if there are many listeners subscribed then it will generate many unwanted exceptions that will just be ignored.
I would prefer doing some kind of check first, like:
if (listener is EventListener<T>)
listener.callback(event)
But I found out that JVM loses information about the generic types after the compilation. I also found out that it can be bypassed using kotlin's inline and reified, however those can not be used on a method that comes from an interface...
So my question is do you know any more elegant way to handle such generic problem?