Как ссылаться на метод экземпляра в вызове конструктора - PullRequest
0 голосов
/ 01 мая 2020

Я пишу некоторый код обработки событий на основе сопрограмм в 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()
    }
}

1 Ответ

3 голосов
/ 01 мая 2020

Вы не можете заставить его скомпилироваться по 3 различным причинам:

  1. Поскольку handleExample является методом экземпляра, вы не можете ссылаться на него в супер-конструкторе, так как экземпляр вашего подкласса еще не создано.

  2. Если вы хотите ссылку на функцию метода экземпляра, вы должны поставить перед ней префикс ::, так что в вашем случае ::handleExample.

  3. Функция handleExample принимает событие типа ExampleEvent, поэтому оно не соответствует типу ввода 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.

...