在以下情况下,我找不到避免未经检查的演员表的方法
class EventBus {
val eventToHandle: MutableMap<KClass<out Event>, Event.() -> Unit> = mutableMapOf()
final inline fun <reified T : Event> register(noinline handler: T.() -> Unit) {
@Suppress("UNCHECKED_CAST")
eventToHandle[T::class] = handler as Event.() -> Unit
}
fun fire(event: Event) {
eventToHandle[event::class]?.invoke(event)
?: throw IllegalStateException("Missing handler for class ${event::class}")
}
}
我的目标是强制地图仅包含具有Event
(或其子类型之一)作为接收器的lambda,但同时我希望引用 actual 注册lambda时实现Event
。
这样,我可以使用其实现的成员而无需每次都进行强制转换。
一个示例(requestId
是RequestExpiredEvent
的字段):
eventBus.register<RequestExpiredEvent> {
requestService.setExpiredByRequestId(requestId)
}
我知道“消费者lambda”没有协方差,并且它们是协变的,但是我想是否有办法。 我发现一个丑陋的解决方法是:
final inline fun <reified T : Event> register(crossinline block: T.() -> Unit) {
val handler: Event.() -> Unit = { this as T; block(this) }
eventToHandle[T::class] = handler
}
谢谢
答案 0 :(得分:0)
这很危险。 SomeEventSubtype.() -> Unit
不是Event.() -> Unit
的子类型。相反。
假设事件是一个开放类,并且您有此子类:
class SubEvent: Event() {
fun hello(): Unit {
println("hello")
}
}
现在您尝试投射它:
val hello: SubEvent.() -> Unit = SubEvent::hello
val helloCasted = hello as Event.() -> Unit
当您尝试调用helloCasted.invoke(Event())
时,它将尝试将您的事件强制转换为子事件时将引发ClassCastException。您无法使用hello
的任何实例作为输入来调用Event
,因为只有SubEvent
才具有hello
的调用功能。
如果您尝试隐式转换它,编译器将捕获此错误:
val hello: SubEvent.() -> Unit = SubEvent::hello
val helloCasted: Event.() -> Unit = hello // compiler error
它确实可以反过来工作。当查看函数的 inputs 时,可以认为类型层次结构是倒置的。
val toStringFun: Event.() -> Unit = Event::toString
val toStringCasted: SubEvent.() -> Unit = toStringFun // OK
如果不在某处进行未经检查的转换,则无法解决此问题,因为您在地图中存储了不同类型的对象。但是您需要将演员表移至fire
函数,以便它知道将其投射到什么内容。您可以在地图中将函数存储为Any
类型,因为无论如何都要转换它们。像这样:
class EventBus {
val eventToHandle: MutableMap<KClass<out Event>, Any> = mutableMapOf()
inline fun <reified T : Event> register(noinline handler: T.() -> Unit) {
eventToHandle[T::class] = handler
}
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Event> fire(event: T) {
(eventToHandle[T::class] as? T.() -> Unit)?.invoke(event)
?: throw IllegalStateException("Missing handler for class ${event::class}")
}
}