Как «объединить» подклассы в экземпляр супертипа? - PullRequest
0 голосов
/ 12 марта 2020

Если у меня есть функция, которая получает Callback в качестве параметра, как я могу вызвать эту функцию с «коллекцией» или «группой» подкласса / производного класса Callback, чтобы, основываясь на полученный параметр, я могу включить / отключить определенную функциональность, если полученный подкласс / производный класс "имеет" или "соответствует" функциональности?

Например,

import kotlin.random.Random

interface Callback

interface FruitsCallback : Callback {
    fun onFruitsDeleted(ignored: Int)
}

interface VegetablesCallback : Callback {
    fun onVegetablesDeleted(ignored: Int)
}

class FruitsStore : FruitsCallback {

    override fun onFruitsDeleted(ignored: Int) {
        throw NotImplementedError()
    }
}

class VegetablesStore : VegetablesCallback {

    override fun onVegetablesDeleted(ignored: Int) {
        throw NotImplementedError()
    }
}

fun delete(callback: Callback) {

    if (callback is FruitsCallback) {
        // Delete 5 fruits
        callback.onFruitsDeleted(5)
    }

    if (callback is VegetablesCallback) {
        // Delete 7 vegetables
        callback.onVegetablesDeleted(7)
    }
}

fun main() {

    val fruitsStore = FruitsStore()
    val vegetablesStore = VegetablesStore()

    val callback: Callback = if (Random.nextBoolean()) {
        fruitsStore + vegetablesStore
    } else fruitsStore

    delete(callback)
}

Ошибка в методе main(), когда я пытаюсь выполнить fruitsStore + vegetablesStore. Это показывает проблему несоответствия типов. Что-то делать с kotlin .plus().

Но тогда как я могу добавить следующие типы вместе при использовании coroutines?

val exceptionHandler = CoroutineExceptionHandler { context, throwable -> println("$throwable") }
val coroutineContext = Dispatchers.Main + SupervisorJob() + exceptionHandler

Я хочу знать как coroutineContext может получить группу экземпляров этих типов и представить их как единое целое.

Я довольно новичок в kotlin. Пожалуйста, объясните:

  • Почему я не могу это сделать?
  • Почему сопрограммы могут это сделать?
  • Как это называется?
  • Как я могу сделать это в приведенном выше примере?

1 Ответ

3 голосов
/ 12 марта 2020

API сопрограммы использует перегрузку оператора , чтобы заставить оператор + делать то, что он обычно не может. Если вы посмотрите на исходный код, функция оператора перегруженного плюса создает новый CoroutineContext, который скопировал значения свойств обоих операндов.

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

operator fun Callback.plus(other: Callback): Callback {
    val callbacks = listOf(this, other)
    return when {
        callbacks.all { it is FruitsCallback } -> object: FruitsCallback {
                override fun onFruitsDeleted(ignored: Int) {
                    callbacks.forEach { (it as FruitsCallback).onFruitsDeleted(ignored) }
                }
            }

        callbacks.all { it is VegetablesCallback } -> object : VegetablesCallback {
                override fun onVegetablesDeleted(ignored: Int) {
                    callbacks.forEach { (it as VegetablesCallback).onVegetablesDeleted(ignored) }
                }
            }

        else -> object: FruitsCallback, VegetablesCallback {
            override fun onFruitsDeleted(ignored: Int) {
                callbacks.forEach { (it as? FruitsCallback)?.onFruitsDeleted(ignored) }
            }

            override fun onVegetablesDeleted(ignored: Int) {
                callbacks.forEach { (it as? VegetablesCallback)?.onVegetablesDeleted(ignored) }
            }
        }
    }
}

Если бы существовала третья реализация обратного вызова, вышеуказанная функция имела бы семь ветвей , И здесь нет никакого принуждения, что вы не забудете обновить эту функцию, если добавите дополнительные реализации.

Тем не менее, ваша версия использования delete() функции *1013* - это запах кода, отказ от использования полиморфизма , В этом случае было бы лучше добавить delete() как функцию в Callback, и каждая реализация может определить для себя, что это должно означать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...