Моя цель - реализовать очень простую шину событий в 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!")
Это работает и полностью пригодно для использования, однако я не удовлетворен этим. .. Приведение слушателя в качестве EventListener всегда будет возвращать что-то, а затем, если l.callback (событие) не сможет обработать тип события, оно выдаст исключение. Поэтому, если подписчики подписаны на большое количество пользователей, они будут генерировать много нежелательных исключений, которые будут просто игнорироваться.
Я бы предпочел сначала выполнить какую-то проверку, например:
if (listener is EventListener<T>)
listener.callback(event)
Но я нашелвыходит, что JVM теряет информацию об универсальных типах после компиляции. Я также обнаружил, что его можно обойти, используя inline и reified , которые не могут быть использованы в методе, который исходит из интерфейса ...
Поэтому мой вопрос: знаете ли вы какой-нибудь более изящный способ справиться с такой общей проблемой?