26

I posted a question (Dagger 2 does not generate the component class (Android, Kotlin)), and after some experiments, it seems that the problem might be due to that Kotlin hides the field.

class CoffeeShop {
    @Inject
    var theCoffee: Coffee? = null
}

The error message is,

:app:kaptDebugKotline: ...\CoffeeShop.java:7:
error: Dagger does not support injection into private fields
e:     private ....Coffee theCoffee;

theCoffee was not private in my source code. But I think Kotlin may be translating

class CoffeeShop {
    @Inject
    var theCoffee: Coffee? = null
}

into Java code of

class CoffeeShop {
    @Inject
    private Coffee theCoffee = null;
    public Coffee getTheCoffee();
    public void setTheCoffee();
}

Can I use field injection in Kotlin?

William Reed
  • 1,717
  • 1
  • 17
  • 30
Damn Vegetables
  • 11,484
  • 13
  • 80
  • 135

1 Answers1

58

I think Kotlin may be translating [...] into Java code of [...]

And you would be correct, that's exactly what happens.

Typically in Kotlin you wouldn't write

@Inject var coffee: Coffee? = null

because when you're going to access coffee, it will never be null. In other words you will always inject the object before accessing it's fields. That makes the operators !! redundant and ? unnecessary. Kotlin has lateinit property modifier to express this.

@Inject lateinit var coffee: Coffee

When you use lateinit the generated field has the same visibility as its getter and setter, in this case public. This makes it work with Dagger.

You can see the result by viewing generated Kotlin bytecode.

Main menu > Tools > Kotlin > Show Kotlin Bytecode

However, even better approach would be injecting the class constructor:

class CoffeeShop @Inject constructor(val coffee: Coffee) {
    //...
}

In this case coffee is not var and can't be reassigned.

Injecting constructor is not an option when the instance is created for you by a framework, Android activity is a good example.


Note: When using qualifiers you have to specify field annotation target on them:

@Inject @field:Named("Arabica") @field:Arabica
lateinit var coffee: Coffee

Edit: You don't need to add the field target when using Dagger 2.25 or newer.


Can I use field injection in Kotlin?

Yes you can. As explained above, field injection is actually applied for lateinit properties.

But you were probably interested in generating and injecting fields without getter/setter in Kotlin.

@JvmField @Inject
var coffee: Coffee? = null
Eugen Pechanec
  • 37,669
  • 7
  • 103
  • 124
  • Actually I saw `lateinit` before posting this question, but if I use 'lateinit', it seemed that I could no longer use field injection but had to use constructor injection, is that correct? The error message was `Coffee cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.`. So, doesn't that make the answer to my question a "no"? – Damn Vegetables Nov 12 '17 at 12:26
  • @DamnVegetables Damn, I completely missed the question :D Check out the updated answer. `but if I use 'lateinit', it seemed that I could no longer use field injection but had to use constructor injection` Now I'm not sure what you mean by `field injection`. Calling `MembersInjector` manually? That works with lateinit. lateinit=field as far as dagger is concerned. – Eugen Pechanec Nov 12 '17 at 13:05
  • Thank you. If you do not mind, where does `Arabica` came from? And the error message, `@Provides- or @Produces-annotated method`, does that mean a provide/produces for `CoffeeShop`? Because a provide method for `Coffee` already exists. (The full source is in the question I mentioned.) – Damn Vegetables Nov 12 '17 at 13:36
  • @DamnVegetables `Arabica` is a made up [custom qualifier](https://google.github.io/dagger/users-guide.html#qualifiers). You probably won't need qualifiers just now. Focus on getting it to work. About the error: Dagger needs to inject `val coffee: Coffee` and for that it needs to know where `Coffee` comes from. As the error hints there are two ways. The [user guide](https://google.github.io/dagger/users-guide.html) explains it best, I think. – Eugen Pechanec Nov 12 '17 at 13:47