首页 > 解决方案 > 如何在构造函数调用中引用实例方法

问题描述

我正在 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()
    }
}

标签: genericskotlincoroutinekotlin-coroutines

解决方案


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

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

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

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

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

解释都在评论里。

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您可以轻松地在所有子类中填充handlers地图EventHandler。 


推荐阅读