【问题标题】:How to reference an instance method in constructor invocation如何在构造函数调用中引用实例方法
【发布时间】:2020-04-30 21:11:09
【问题描述】:

我正在用 Kotlin 编写一些基于协程的事件处理代码,它进展顺利。我在各种事件处理程序中有代码,它们做同样的事情,我试图把这段代码放在一个地方。我被困在以下问题上。这个想法是子类可以通过提供类到方法的映射来指定可以处理的事件类型。我无法编译它。有没有办法使这项工作?有更好的方法吗?谢谢。


abstract class EventHandler(private val handlers: Map<KClass<out Event>, suspend (Event) -> Unit>) {
    suspend fun handle(event: Event) {
        val handler = handlers[event::class]
        if (handler != null) {
            handler(event)
        } else {
            throw IllegalStateException("No handler configured for $event")
        }
    }
}

data class ExampleEvent(private val id: String): Event

class ExampleHandler(): EventHandler(mapOf(ExampleEvent::class to handleExample)) {
                                                                  ^^^^^^^^^^^^^ - compile error
    suspend fun handleExample(event: ExampleEvent) {
        TODO()
    }
}

【问题讨论】:

    标签: generics kotlin coroutine kotlin-coroutines


    【解决方案1】:

    由于 3 个不同的原因,您无法编译它:

    1. 由于handleExample是一个实例方法,你不能在超级构造函数中引用它,因为你的子类的实例还没有创建。

    2. 如果你想要一个实例方法的函数引用,你应该在它前面加上::,所以在你的情况下是::handleExample

    3. 函数handleExample 接受ExampleEvent 类型的事件,因此它不符合输入类型Event,在这种情况下,您需要进行强制转换。

      李>

    也就是说,您的问题有一个解决方案,它解决了上述 3 点,并且为每个 EventHandler 子类重复该样板的负担。

    解释都在cmets上。

    inline fun <reified T : Event> typedCoroutine(crossinline block: suspend (T) -> Unit): Pair<KClass<out Event>, suspend (Event) -> Unit> =
        // Since the type is reified, we can access its class.
        // The suspend function calls the required suspend function casting its input.
        // The pair between the two is returned.
        T::class to { event -> block(event as T) }
    
    abstract class EventHandler {
        // Require the subclasses to implement the handlers.
        protected abstract val handlers: Map<KClass<out Event>, suspend (Event) -> Unit>
    
        suspend fun handle(event: Event) {
            val handler = handlers[event::class]
            if (handler != null) {
                handler(event)
            } else {
                throw IllegalStateException("No handler configured for $event")
            }
        }
    }
    
    class ExampleHandler : EventHandler() {
        // The type of typedCoroutine is inferred from handleExample.
        override val handlers: Map<KClass<out Event>, suspend (Event) -> Unit> = mapOf(typedCoroutine(::handleExample))
    
        suspend fun handleExample(event: ExampleEvent) {
            TODO()
        }
    }
    

    使用typedCoroutine,您可以在所有EventHandler 子类中轻松填充handlers 映射。

    【讨论】:

    • 这太棒了!它完美地工作。有什么办法可以简单地声明子类,以便他们只列出他们的方法?所以不是 mapOf(typedCoroutine(::handleExample), typedCoroutine(::handleExample2)) 我只想声明 listOf(::handleExample, ::handleExample2) ?
    • @B255 不幸的是,没有办法这样做,因为创建一个列表,你会失去创建内部地图所需的reified T。我所说的只是一种简化,因为理论上您可以列出方法并且仍然具有类型,但它将是这些方法的所有输入的基本类型,这在您的情况下没有用。
    猜你喜欢
    • 2022-11-23
    • 1970-01-01
    • 2010-09-23
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 2012-07-11
    相关资源
    最近更新 更多