Correct usage of Dagger 2 @Named annotation in Kotlin

That’s my first post in Medium. Love this place. Such great people and ideas!


Let me warn you about something small but subtle in Dagger 2 that bit me recently and save you from loosing few hours before getting what’s going on.

Suppose you are injecting a member variable:

In Java:

class MyPresenter {
@Inject @Named("api1") Api api;
...
}

In Kotlin 1.1 the same one must have a different syntax:

class MyPresenter {
@field:[Inject Named("api1")]
internal lateinit var api: Api;
...
}

The reason is because in Kotlin annotations need to be slightly more complicated in order to work as expected from Java perspective. That’s coming from the fact that one Kotlin element may be a facade of multiple Java elements emitted in the bytecode. For example a Kotlin property is a facade of an underlying Java member variable, a getter and a setter. You annotate the property but what Dagger expects to be annotated is the underlying field.

That’s why JetBrains added additional syntax for specifying the use-site target explicitly — specifiers like field and several others (Full list in the docs: http://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets).

Without the `field[Named(api1”)]` in example above the @Inject annotation will be attached to the property not the field.

Injecting primitives (using @set)

Now let’s try to inject not an object but a primitive type like `Int`:

@field:[Inject Named("logoIcon")] var logoIcon: Int = 0

aaand… oops:

error: Dagger does not support injection into private fields
e: private int logoIcon;

It turns out that primitives are handled differently. The error should come from the missing lateinit specifier. Because in Kotlin we can’t use lateinit with primitive variables. Apparently lateinit makes the underlying field public. Well, then let’s try with “setter injection”.

@set:[Inject Named("logoIcon")] var logoIcon: Int = 0

To grasp it easier think in Java terms. In Java it’s equivalent to:

private int logoIcon = 0;
public int getLogoIcon() { return this.logoIcon; }
@Inject
@Named("logoIcon")
public void setLogoIcon(int logoIcon) { this.logoIcon = logoIcon; }

Now it works. Dagger is so flexible that it can inject also by calling a setter, not only via constructor parameters and fields.

TL;DR: In Kotlin we inject named fields like this:

@field:[Inject Named("api1")] internal lateinit var api: Api
// or if you inject a primitive
@set:[Inject Named("logoIcon")] var logoIcon: Int = 0

NOT:

@Inject @Named("api1") internal lateinit var api: Api

Have fun kotling :)