Search code examples
kotlindependency-injectiondagger-2dependency-managementdagger

Dagger: lateinit property has not been initialized


There already several QAs on this question, but for me they seem to talk about different things (Android's Kotlin + Dagger2, in most of the case) and not applying my specific case.

I'm learning Dagger by reading this blog post. Instead using the Java code from that blog, I'm trying to use Kotlin.

So, the House.kt as interface:

interface House {
    fun prepareForWar()
    fun reportForWar()
}

The as-is BoltonsDagger.kt:

class BoltonsDagger @Inject constructor(): House {
    override fun reportForWar() {
        println("${this.javaClass.simpleName} reporting..")
    }

    override fun prepareForWar() {
        println("${this.javaClass.simpleName} prepared for war")
    }
}

The as-is StarksDagger.kt:

class StarksDagger @Inject constructor(): House {
    override fun prepareForWar() {
        println("${this.javaClass.simpleName} prepared for war")
    }

    override fun reportForWar() {
        println("${this.javaClass.simpleName} reporting..")
    }
}

Finally the WarDagger.kt with main function:

class WarDagger @Inject constructor() {
    @Inject lateinit var starks: StarksDagger
    @Inject lateinit var boltons:BoltonsDagger

    fun prepare() {
        starks.prepareForWar()
        boltons.prepareForWar()
    }

    fun report() {
        starks.reportForWar()
        boltons.reportForWar()
    }
}

fun main() {
    val war = WarDagger()
    war.prepare()
    war.report()
}

With the error: Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property starks has not been initialized.


Solution

  • First, change the WarDagger to following:

    class WarDagger @Inject constructor(
        private val starks: StarksDagger,
        private val boltons: BoltonsDagger
    ) {
        ...
    }
    

    With this you are making Dagger aware as to how an instance of WarDagger should be created. There is no need to annotate constructor fields with @Inject.

    I cannot locate dagger component in the question, so let's create one:

    @Component
    interface WarComponent {
        fun provideWarDagger(): WarDagger
    }
    

    We are making an instance of WarDagger accessible through the component, so that clients can get an instance of it.

    Now, inside main instead of manually creating an instance of WarDagger you should be retrieving it from the dagger component, because that's the whole reason that you are using a DI framework, isn't it? Clients should not be aware how dependencies are being created:

    fun main() {
        val component = DaggerWarComponent.create()
        val war = component.provideWarDagger()
        war.prepare()
        war.report()
    }