Kotlin - Динамически создавать экземпляры всех внутренних классов - PullRequest
1 голос
/ 11 февраля 2020

Вот ситуация, которую я хочу решить - служба потребляет JSON и выполняет некоторую обработку. Основываясь на строковом значении в одном из полей JSON, я хочу создать один из нескольких классов, реализующих интерфейс.

По сути, я пытаюсь дать входу JSON возможность контролировать, какие функции использовать.

Упрощенная версия моего текущего решения выглядит следующим образом

interface A{
    val name: String
    fun doSomething()
}

class choseImplementation(val jsonChoice: String){
    inner class B: A{
        override val name = "B"
        override fun doSomething(){// Do B things}
    }

    inner class C: A{
        override val name = "C"
        override fun doSomething(){// Do C things}
    }

    init {
        // Instantiate B and C
        // Compare B.name and C.name with jsonChoice
        // Choose the name that matches jsonChoice and expose the instantiated class
        // The exposed class will be used in downstream processing
    }
}

selectedImplementation задается строка и предоставляет правильную реализацию, которая будет использоваться системой.

Сейчас я вручную создаю экземпляр каждого класса в функции init и затем сравниваю, что означает, что мне нужно изменить код в двух местах, добавить класс D, а затем включить его в функцию init

Я хочу иметь возможность добавить новый внутренний класс (D реализует A) и автоматически добавить его в процесс создания и выбора.

Приветствуются решения вне этого шаблона

Спасибо !

1 Ответ

3 голосов
/ 11 февраля 2020

Вы можете использовать свойство nestedClasses KClass<T>, чтобы получить все классы, вложенные в определенный класс. Затем вам нужно будет отфильтровать результат, чтобы получить только классы inner с использованием свойства isInner .

Затем создать экземпляры классов, как правило, используя primaryConstructor или нахождение конструктора нужной сигнатуры, но, поскольку это будет класс inner, обратите внимание, что все его конструкторы имеют дополнительный первый (и, возможно, единственный) параметр, которому необходимо передать экземпляр внешнего class.

Вместе это будет выглядеть следующим образом:

class ChooseImplementation(val jsonChoice: String) {
    /* Inner classes omitted... */ 

    init {
        val innerClass = this::class.nestedClasses
            .filter { it.isInner }
            .find { it.simpleName == jsonChoice }
            ?: error("Could not find inner class $jsonChoice")

        val instance = innerClass.primaryConstructor!!.call(this)

        /* Use the instance... */
    }
}

Обратите внимание, что для использования Kotlin API отражения необходимо иметь зависимость от kotlin-reflect.

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