I have two classes that I'm able to have Dagger find and inject for me to use successfully:
TrackEvent
class TrackEvent @Inject constructor(
private val getTrackingProperties: SomeClass
) : UseCase<Boolean, TrackingEvent> {
override suspend operator fun invoke(params: TrackingEvent): Boolean {
return true
}
SomeClass (note: used as a dependency in TrackEvent
)
class SomeClass @Inject constructor() {
override suspend operator fun invoke(): UserTrackingPropertiesResult {
return UserTrackingPropertiesResult()
}
}
TrackEvent
has an entry in an @Module
annotated interface because it's an implementation of the UseCase
interface:
@Component(modules = [MyModule::class])
interface ShiftsComponent {
fun inject(homeFragment: HomeFragment)
}
@Module
interface MyModule {
@Binds
fun bindsTrackEventUseCase(useCase: TrackEvent): UseCase<Boolean, TrackingEvent>
}
Use Case interfaces
interface UseCase<out T, in P> {
suspend operator fun invoke(params: P): T
}
interface NoParamUseCase<out T> {
suspend operator fun invoke(): T
}
What I'd like to do is to inject an interface into TrackEvent
instead of the concrete SomeClass
. So I make SomeClass
implement a NoParamUseCase
class SomeClass @Inject constructor(): NoParamUseCase<UserTrackingPropertiesResult> {
override suspend operator fun invoke(): UserTrackingPropertiesResult {
return UserTrackingPropertiesResult()
}
}
update TrackEvent
to inject the interface:
class TrackEvent @Inject constructor(
private val getTrackingProperties: NoParamUseCase<UserTrackingPropertiesResult>) : UseCase<Boolean, TrackingEvent> {
override suspend operator fun invoke(params: TrackingEvent): Boolean {
return true
}
}
…and update MyModule
to inform Dagger of which implementation I'd like to use:
@Module
interface MyModule {
@Binds
fun bindsTrackEventUseCase(useCase: TrackEvent): UseCase<Boolean, TrackingEvent>
// New
@Binds
fun bindsSomeClass(useCase: SomeClass): NoParamUseCase<UserTrackingPropertiesResult>
}
Dagger now claims that there is a missing binding and that I need to declare an @Provides
annotated method:
error: [Dagger/MissingBinding] com.myapp.core.domain.usecase.NoParamUseCase<? extends com.myapp.core.tracking.UserTrackingPropertiesResult> cannot be provided without an @Provides-annotated method.
public abstract interface MyComponent {
^
com.myapp.core.domain.usecase.NoParamUseCase<? extends com.myapp.core.tracking.UserTrackingPropertiesResult> is injected at
com.myapp.tasks.tracking.domain.usecase.TrackEvent(getTrackingProperties, …)
…
As far as I can tell, this isn't true:
@Binds
in this instance, replacing this with @Provides
and manually providing dependencies here yields the same error.TrackEvent
class and this works.@Binds
declaration.This is different to this question as there's no ambiguity as to which implementation I'm asking Dagger to use in the way that there would be if I had two or more implementations of the same interface.
Why would I get this error now?
According to dagger error message, it expects covariant type NoParamUseCase<? extends UserTrackingPropertiesResult>
, but DI module provides invariant NoParamUseCase<UserTrackingPropertiesResult>
. To generate appropriate signature for provide method you can change it like this:
@Binds fun bindsSomeClass(useCase: SomeClass): NoParamUseCase<@JvmWildcard UserTrackingPropertiesResult>
After that your code should be compiled successfully.