Open
Description
Opening this as a tracking bug for all kotlin related documentation that we should be add/best practices that we should call out to make using Dagger w/ Kotlin easier.
One example: How to achieve the effect of static @Provides
in Kotlin.
Feel free to comment new ideas, but don't make "me too" or "i agree with XYZ" comments.
Activity
ZacSweers commentedon Oct 16, 2017
Edit: This resolved in Dagger 2.25+ by 646e033
If you have injected properties (as "fields"), qualifiers must have
field:
designation.Good
Bad
Forgetting this can lead to subtle bugs if an unqualified instance of that type also exists on the graph at that scope, as that's the one that will end up being injected. This would make a great lint as well
ZacSweers commentedon Oct 16, 2017
Edit: Objects are handled in Dagger 2.25+. Companion objects are handled in Dagger 2.26+.
Static provides can be achieved via
@JvmStatic
. There are two scenarios I see this come up:top-level
object
sIf you have an existing class module, things get a bit weirder
The way this works is as follows:
companion object
must also be annotated as@Module
DataModule
class. Dagger will see those and treat them like regular static fields. Dagger will also see them in the companion object, but that "module" will get code gen from dagger but be marked as "unused". The IDE will mark this as such, as theprovideDiskCache
method will be marked as unused. You can tell IntelliJ to ignore this for annotations annotated with@Provides
via quickfixI sent Ron a braindump once of how dagger could better leverage this, i.e. no requirement for JvmStatic and just call through to the generated
Companion
class, but I think that's out of the scope of this issue :). I've been meaning to write up a more concrete proposal for how that would work.ZacSweers commentedon Oct 16, 2017
Another gotcha is inline method bodies. Dagger relies heavily on return types to connect the dots. In kotlin, specifying the return type is optional sometimes when you can inline the method body. This can lead to confusing behavior if you're counting on implicit types, especially since the IDE will often try to coerce you into quickfixing them away
That is, you could write in one of four ways
The first function is valid, but
DiskCache
is what's on the DI graph there because that's the inferred return type. The first three functions are functionally identical.The fourth function is also valid, but now
Cache
is what's on the graph andDiskCache
is just an implementation detail. The fifth function is functionally identical to the fourth.The IDE will try to suggest inlining the fourth one. You can do so, but be mindful of potentially changing return types if you also drop the explicit return type.
JakeWharton commentedon Oct 16, 2017
ZacSweers commentedon Oct 16, 2017
Good point, tweaked the wording to make it clear that it's only if you drop the return type and added more examples
tasomaniac commentedon Oct 16, 2017
@JvmSuppressWildcards
is incredibly useful when you are injecting classes with generics. Can be handy when using multimap injection.jhowens89 commentedon Oct 17, 2017
What a great thread! I was fighting this fight this last weekend. Here is the syntax for injection that's both qualified and nullable:
@field:[Inject ChildProvider] @JvmField var coordinatorProvider: CoordinatorProvider? = null
janheinrichmerker commentedon Oct 26, 2017
@tasomaniac #807 has also cost me quite some time to debug.
Doesn't look nice but at least it works:
(taken from https://stackoverflow.com/a/43149382/2037482)
charlesdurham commentedon Nov 27, 2017
As an add-on from @heinrichreimer, the same thing is required when injecting functions that have parameters:
InvisibleGit commentedon Dec 2, 2017
With Kotlin 1.2 we can use array literals in annotations, ditching
arrayOf()
call in them@Component(modules = [LibraryModule::class, ServicesModule::class])
guelo commentedon Jan 23, 2018
As far as I can tell Dagger is unable to inject Kotlin functions. This fails
error: kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,java.lang.Integer> cannot be provided without an @Provides- or @Produces-annotated method.
Theoretically you could provide a kotlin.jvm.functions.Function2 but I failed to make it work. Even with this
it still says a Function2 cannot be provided.
JakeWharton commentedon Jan 23, 2018
gildor commentedon Feb 8, 2018
I understand that this issue about "best practices", but on Reddit AMA @ronshapiro mentioned that this issue the place to collect kotlin-specific features.
There is a couple possible kotlin-specific features that require work with Kotlin Metadata, but would be very useful for Kotlin projects and make dagger usage even more pleasant
typealias
as an alternative for@Named
and custom@Qualifier
annotations. It's completely compiler feature, but you can get typealias name from @metadata annotation, so can use it to detect different qualifiers of the same class@JvmSuppressWildcards
. This proposal can be extended to other generics, like for Lists and so on, but it not always required behavior, but in the case of lambdas I don't see any good reason do not consider any lambda (kotlin.jvm.functions.* interface) as generic without a wildcard. It will make lambda injection much less wordy. Also, works pretty well withtypealias
as qualifier.65 remaining items