The missing guide to injecting into private val properties in Kotlin.

Edit Feb 2020:

This approach does have some issues when introducing modularization with dynamic feature modules, where multiple components are introduced and the modules setup their injection outside of the application class. That being said, it’s a great solution for single module apps and I’m sure there exists a tweak to make it work across modules, I just haven’t found it yet.

Original article:

Normally when using dependency injection, the preference is to use constructor injection wherever possible. However as most Android devs are well aware, we can’t do that when it comes to system components such as Activities, Services, and Fragments. In those situations we are forced to use field/property injection. Property injection using JSR-330 @Inject annotations in Kotlin isn’t a great experience. Thanks to Kotlin’s stricter type system, the only way to use JSR-330 annotations on properties is to declare them as lateinit var properties. I’m a fan of using constant val properties wherever possible so I always look for better alternatives to this.

Under RoboGuice I wrote a small set of extensions that allowed us to use it in an idiomatic way in Kotlin. This made it easy to avoid using lateinit var properties by instead using a lazy property delegate that pulls the dependency from RoboGuice programmatically:

I’m still new to a lot of Dagger’s idiosyncrasies, particularly the Component part of its architecture. So while migrating over to Dagger it wasn’t immediately obvious to me that I could use the top level component to achieve the same ends.

Since I was doing this for an Android app it is easy (and documented as such) to store a reference to the component in the Application or Activity classes. As far as I’m aware you can call DaggerMyAppComponent.builder()...build() as many times as you want and get the same instances, but for simplicity’s sake the following code is going to assume you call it once, store it in the Application class and surface it via a getter. (Correction: The above statement is not always true. If you call the builder methods with different arguments then the resulting component tree will be different).

First we write some extension functions for our Context and Fragment classes:

The first function simply defers to the second using the Fragment’s context . Both functions take a lambda with receiver that operates on the top level component, takes no arguments, and produces a value. This is invoked on the component retrieved via the application context within the scope of a lazy property delegate. Both functions using reified types make for a nicer callsite API.

In order to make use of these, you will need to add a function declaration to your component for any dependency that will be injected using field injection.

This all combines into the following callsite API: