Kotlin `object` initialization order leads to unexpected null instance

Viewed 220

Consider the following code:

sealed class DataType<T : Any> {
    abstract fun inputToType(input: String): T
    abstract fun typeToSql(value: T): String

    companion object {
        val all = listOf(StringDt, LongDt)
    }
}

object StringDt : DataType<String>() {
    override fun inputToType(input: String) = input
    override fun typeToSql(value: String) = "\"${value}\""
}

object LongDt : DataType<Long>() {
    override fun inputToType(input: String) = input.toLong()
    override fun typeToSql(value: Long) = value.toString()
}

val dataTypeList = listOfNotNull(StringDt, LongDt)

println(dataTypeList)
println(DataType.all)

Things to consider:

  • object as per documentation (and my understanding as well) is singleton and always instantiated
  • the two objects (StringDt and LongDt) are quite similar

The result of println(DataType.all) shows that one of the objects are not initialized. How is that possible? I would expect all the list elements to be initialized.

IntelliJ version: CE 2020.2 Kotlin plugin version: 1.4.0-release-IJ2020.2-1

Here's a running example which shows that the static list has a null element, while the non-static one contains both objects initialized.

1 Answers

It happens due to cyclical static initializations. It's pretty hard to explain this problem in two words but you can read about it here.

To fix this behavior you can change all initialization like this:

val all by lazy { listOf(StringDt, LongDt) }
Related