Dagger Lazy during constructor injection
Asked Answered
R

1

7

I realize that the recommended way of accomplishing Lazy injection with Dagger is to add Lazy to a field injection point. For instance,

class Foo {
    @Inject lateinit var bar: Lazy<Bar>

    fun useBar() = bar.get().doSomething()
}

What about using constructor injection? I have not seen anyone doing it.

class Foo @Inject constructor(private val fizz: Fizz,
                              private val bar: Lazy<Bar>) {    
    fun useBar() = bar.get().doSomething()
}

To summarize when doing Dagger lazy injection, can I use Lazy<Bar> in a constructor? Or is my only option to move Lazy<Bar> to a field injection while keeping other non-Lazy dependencies in the same class injected via the constructor?

Thanks for any pointers!

Reptile answered 1/7, 2018 at 22:48 Comment(3)
Have you tried?Bardwell
If you immediately call lazy.get() in the constructor, why not just inject the plain object?Suppurative
Good point but I actually don't call it in the constructor but only conditionally. The examples above may not be that clear.Reptile
B
9

Constructor injection is no different from regular field injection, Lazy-wrapped constructor arguments are acceptable.

The only thing you need to make sure is to expose the dependency via module somewhere in the dependency graph.

Bitters answered 2/7, 2018 at 9:40 Comment(6)
I am able to do it but since I have not seen any examples of doing via constructor injection was not sure if the object graph is actually changing as expected (the Lazy object only gets added to the object graph after get() is called)Reptile
@liminal: If your object (Bar) is not scoped (e.g. @Singleton), then getting it won't change the object graph; across all consumers, direct injection of Bar will create an instance, Provider<Bar> will return a new instance each time you call get, and Lazy<Bar> will generate a new instance on get and always return the same one. If your object is scoped, then calling get will cause the object to be created for everyone, but no differently than if you had injected it aside from the delay. (For a scoped object, Provider and Lazy are equivalent.)Strobila
@JeffBowman correct, having exposed scoped dependency, the Lazy-wrapped instance of DoubleCheck provider accessing the dependency is generated so we have a sort of double dependency caching done by both the Lazy wrapper and the provider(once when the dependency is accessed for the first time). Things differ if we expose the dependency without scope - we just cache the dependency with the Lazy wrapper only.Bitters
@dawidgdanski Right, but I'll do you one better: There's no double dependency caching, because the DoubleCheck object that caches the value implements both Provider and Lazy, and specifically avoids caching a value that has already been cached. In that sense, a lazy injection of a scoped value doesn't involve creating a separate instance to track the lazy result.Strobila
Thanks. So I am doing it correctly then. The object that's injected in the constructor and wrapped in Lazy is Singleton scopedReptile
I have a class with @Singleton annotation and I wanted to understand if there is any difference if I inject an object lazily through constructor vs field using dagger?Dulcinea

© 2022 - 2024 — McMap. All rights reserved.