Инициализация сопутствующего объекта после внутренних объектов - PullRequest
0 голосов
/ 01 марта 2019

Допустим, я хочу создать закрытый класс, заполненный некоторыми объектами.Затем я хочу создать список всех таких объектов, поэтому я создаю список в объекте-компаньоне:

fun main() {
    println(Color.Blue)
    println(Color.allColors)
}

sealed class Color {
    object Red : Color();
    object Blue : Color();

    companion object {
        val allColors = listOf(
                Red,
                Blue
        )
    }
}

Однако проблема с приведенным выше кодом заключается в том, что при первом непосредственном вызове Color.Blue объект-компаньонинициализируется до Blue и, следовательно, результирующий список содержит [Red, null].Это вдвойне проблематично, поскольку Котлин предполагает, что список содержит ненулевые значения.

Я знаю, что приведенный выше пример достаточно прост, чтобы я мог заменить sealed class на enum, но это всего лишь упрощенный пример.Во многих случаях полезно использовать запечатанные классы над перечислениями (например, когда вам нужно добавить параметры типа для отдельных объектов).

Каков наилучший способ решить эту проблему с наименьшим количеством шаблонов и распределениемобъекты?Я предложил два обходных пути, но мне не нравится ни один из них:

Ленивый

fun main() {
    println(Color.Blue)
    println(Color.allColors)
}

sealed class Color {
    object Red : Color();
    object Blue : Color();

    companion object {
        val allColors by lazy {
            listOf(
                    Red,
                    Blue
            )
        }
    }
}

Вышеупомянутое решение выглядит хорошо и не вызывает много плиты котла, но оно создает одиндополнительный объект, который живет навсегда для каждого свойства в объекте-компаньоне.Мне также нужно было бы повторить ключевое слово lazy для любых дополнительных свойств.

Перемещение инициализации в другой объект

fun main() {
    println(Color.Blue)
    println(Color.allColors)
}

sealed class Color {
    object Red : Color();
    object Blue : Color();

    private object Initializer {
        val allColors = listOf(
                Red,
                Blue
        )
    }

    companion object {
        val allColors: List<Color>
            get() = Initializer.allColors
    }
}

Этот метод имеет преимущество, заключающееся в создании только одного объекта для всех свойств в объекте-компаньоне,но это создает много дополнительных шаблонов.

Есть ли лучший способ добиться этого?

РЕДАКТИРОВАТЬ: Существует проблема на трекер проблем Kotlin для этого случая: https://youtrack.jetbrains.com/issue/KT-8970

1 Ответ

0 голосов
/ 05 марта 2019
sealed class Color(var meh:Int) {
    object Red : Color(10)
    object Blue : Color(20)

    companion object {
        private var colorsList:List<Color>? = null
        val allColors:List<Color>
            get() = colorsList?:run{
                colorsList = listOf(
                        Red,
                        Blue
                )
                colorsList!!
            }
    }
}

Это синглтон всегда.Это просто еще один способ сделать это.Но объект Initializer выглядит чище.

...