在内部对象之后初始化伴侣对象

时间:2019-03-01 08:48:31

标签: kotlin initialization-order sealed-class

假设我要创建一个密封的类,其中填充了一些对象。然后,我想创建所有此类对象的列表,因此我在伴侣对象中创建列表:

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]。这是双重问题,因为Kotlin假定list包含非空值。

我知道上面的例子很简单,可以用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
            )
        }
    }
}

上述解决方案看起来不错,不会引起太多重复,但它会创建一个附加对象,该附加对象对于伴随对象中的每个属性都将永久存在。我还需要在其他任何属性上重复使用惰性关键字。

将初始化移动到另一个对象中

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 :(得分:0)

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对象看起来更干净。