Preventing mistakes with property declaration order

Viewed 26

I'm looking for a way to prevent problems in Kotlin with the order of property initialization. One of my biggest loves of Kotlin is all the ways you can lean on the compiler to prevent errors. But in this case it falls flat. Here's an example

class Test{
    val b = computeB()
    val a = computeA()

    private fun computeA() = 4
    private fun computeB() = a + 1
}

fun main(args: Array<String>) {
    val a = Test()
    println("value of b: " + a.b)
    println("value of a: " + a.a)
}

In this scenario, you get a = 4 and b = 1. But you should get b = 5. I understand what's going on, a is not initialized yet so it defaults to 0 when it's accessed. I was hoping that Kotlin would at least throw an exception to let me know.

This is a trivial example, but in large classes with lots of code it's easy to accidentally do this. My question is, what is the best style/paradigm/pattern that I can use in Kotlin to ensure this kind of mistake doesn't happen. I'm looking for some sort of rule that I can hold myself, and my teammates to. Like "you should construct properties in this way, because otherwise you open the door to these kinds of bugs".

1 Answers

The thing that you really shouldn't do here is to call methods on this in the property initialiser, because as you have identified, this at that point is not fully initialised, and the method will see unspecified values for some of the properties it depends on.

So the first thing is to avoid calling instance methods, if you can.

I know this is just an example, but there is really no need to call a method just to say "4" and "a + 1". If you inline both of your methods, you get:

val b = a + 1
val a = 4

If the initialisation does need to call an instance method, use lazy:

val b by lazy { computeB() }
val a = 4

Another idea is to rewrite your functions to take the things they depend on as parameters:

private fun computeB(a: Int) = a + 1

This way, you will get an error when you try to write:

val b = computeB(a)
val a = 4
Related