Kotlin data object becomes null

48 Views Asked by At

I am sealed class in Kotlin and 4 data objects. I want to use data objects to populate bottom tabs. However one of data objects return null.

I have this class:

sealed class TabanSevkHedefiMuta(
    val ibre: Int,
    val hedef: String,
    val nisaneSerbest: ImageVector,
    val yafta: String
) {
    data object Sinif : TabanSevkHedefiMuta(
        ibre = 1,
        hedef = "sinif",
        nisaneSerbest = Icons.Filled.School,
        yafta = "Sınıf"
    )

    data object Talebe : TabanSevkHedefiMuta(
        ibre = 2,
        hedef = "talebe",
        nisaneSerbest = Icons.Filled.Person,
        yafta = "Talebe"
    )

    data object Vazife : TabanSevkHedefiMuta(
        ibre = 3,
        hedef = "vazife",
        nisaneSerbest = Icons.Filled.Book,
        yafta = "Vazife"
    )

    data object Tavir : TabanSevkHedefiMuta(
        ibre = 4,
        hedef = "tavir",
        nisaneSerbest = Icons.Filled.Handshake,
        yafta = "Tavır"
    )

    companion object {
        val hedefler = listOfNotNull(Sinif, Talebe, Vazife, Tavir)
    }

}

when I try to iterate hedefler like this:

TabanSevkHedefiMuta.hedefler.forEach { tabanSevkHedefiMuta ->
    Ikazci.izahEt(izahat = "tabanSevkHedefiMuta: ${tabanSevkHedefiMuta.yafta}")
}

only 3 elements are printed to log, like below:

tabanSevkHedefiMuta: Talebe
tabanSevkHedefiMuta: Vazife
tabanSevkHedefiMuta: Tavır

how can this happen? I mean how can Sinif become null? Thanks for help.

2

There are 2 best solutions below

0
Sam On BEST ANSWER

This type of problem is known as "use before initialization". Each object in your application needs to be initialized before it can be used. Initialization involves filling in the initial values of the object's properties, as well as executing the code in the init block, if there is one.

When one object contains a reference to another, the order of initialization becomes important. Since your data objects effectively reference their class, and the class references its companion object, it seems there's a cyclic dependency which Kotlin isn't able to completely resolve. The first time the code encounters a reference to Sinif, it will try to initialize the Sinif object, which first means initializing the superclass, which first means initializing the companion object, which—thanks to the hedefler list—first means initializing Sinif

An object which hasn't yet been initialized will appear to be null. In your code, Kotlin tries to break the cycle by initializing the companion object first. This captures the value of Sinif before it has actually been initialized. By the time Sinif becomes non-null, it's too late—the companion object has already put the null value into the list.

It's not clear why the remaining objects manage to initialize themselves in time, but it probably comes down to some implementation details in the way Kotlin detects and resolves these initialization cycles.

There are quite a few open issues in the Kotlin issue tracker related to object initialization problems of this type. This specific issue has already been raised as KT-21614, and it looks like there aren't any plans to fix it.

Your best bet is just to try and remove the cyclic dependency. As you suggested, a lazy property is one way to avoid the cycle, since it delays the creation of the list until after the companion object has finished its initialization. You could also use a computed property that builds a new list every time it's accessed.

0
ysrtyfn On

Using by lazy is a method that alleviates this error, like this:

companion object {
    val hedefler: List<TabanSevkHedefiMuta> by lazy {
        listOfNotNull(Sinif, Talebe, Vazife, Tavir)
    }
}

But if possible, I want to know why this happens.

Resource