kotlinlateinit

Avoiding lateinit


I've got a class that has a structure similar to this:

class A {
    lateinit var x: String
    lateinit var y: String
    lateinit var z: String

    init {
        reset()
    }

    fun reset() {
        val temp = some complex calculation...
        x = temp.let {
            some other complex calculation...
        }
        y = temp.let {
            some other complex calculation...
        }
        z = temp.let {
            some other complex calculation...
        }
    }
}

If the body of reset was in init, I wouldn't need to declare the variables lateinit. But because I'm calling reset() in init, and I don't refer to the variables directly, the compiler doesn't see that the variables are definitely initialized, and therefore they have to be declared lateinit.

Is it possible to somehow tell the compiler that the variables are in fact initialized?

I've tried declaring reset as inline but it still wouldn't compile without lateinit.


Solution

  • It seems from this comment that it is a priority for you to avoid recalculation or object-lifetime storing of the dependency which initializes your properties. To achieve that, you can let the primary constructor accept a simple argument that's not a property of the class (this is an adaptation to this solution):

    class A(dependency: Dependency) {
         private val x = dependency.getX()
         private val y = dependency.getY()
         private val z = dependency.getZ()
    
    }
    

    note how the private val is no longer present for the dependency constructor argument. Now, judging from your code snippet, the value of dependency (or temp in your snippet) is not supposed to be a parameter but rather to be calculated once the class is instantiated. To achieve this, you can use a secondary constructor or a companion object factory function that calls the primary and injects the dependency that was just calculated. If you want to prohibit injecting the dependency from the outside, you can additionally make your primary constructor private:

    class A private constructor(dependency: Dependency) {
        private val x = dependency.getX()
        private val y = dependency.getY()
        private val z = dependency.getZ()
        
        constructor() : this(aFunctionThatCalculatesDependency()) {}
    

    again, if the logic for calculating dependency cannot be represented by a single expression that you can pass to this(), use a companion object factory function instead. If you really want something that still looks like a constructor, though, there's also the pseudoconstructor pattern (invoke() operator function on the companion object)