Given the interface:
interface Example<T>
The implementation:
class ExampleImpl: Example<List<String>>
The module:
@Module
interface ExampleModule {
@Binds
fun provideExample(example: ExampleImpl): Example<List<String>>
}
The error @Binds methods must have only one parameter whose type is assignable to the return type is thrown at compile-time.
Important notes
@Binds and not with @Provides@Module
class ExampleModule {
@Provides
fun provideExample(example: ExampleImpl): Example<List<String>> = example
}
T is used as a covariant type parameter in the implementation of Example.class ExampleImpl: Example<MutableList<String>>
@Module
interface ExampleModule {
@Binds
fun provideExample(example: ExampleImpl): Example<MutableList<String>>
}
This seems relevant for #900. Probably has something to do with Kotlin's declaration site variance. The quick fix is probably to use @JvmSuppressWildcards but let's keep this open until we have a better solution.
I tried with @JvmSuppressWildcards but it doesn't help in this case.
It's still needed using the @Provides annotation.
Example:
@Module
class ExampleModule {
@Provides
fun provideExample(example: ExampleImpl): Example<List<String>> = example
}
class Consumer @Inject constructor(private val example: @JvmSuppressWildcards Example<List<String>>)
You might also need it on the bind. The bytecode window will show the signature of both places.
I tried to add it also on the bind with no success.
Decompiling the bytecode, I obtain:
Consumer
_signature Lcom/package/Example
ExampleImpl
_signature Ljava/lang/Object;Lcom/package/Example
Module
_signature (Lcom/package/ExampleImpl;)Lcom/package/Example
The only way I avoided the error @Binds methods must have only one parameter whose type is assignable to the return type is declaring the implementation as:
class ExampleImpl : Example<List<@JvmSuppressWildcards String>>
But I can't inject it anymore.
After this last change, I obtain:
Consumer
_signature Lcom/package/Example
ExampleImpl
_signature Ljava/lang/Object;Lcom/package/Example
Module
_signature (Lcom/package/ExampleImpl;)Lcom/package/Example
The types seems to conform after the last change, but it doesn't work yet.
Ok. I'd like to try it out and see what the bytecode says, hopefully tonight. In theory nothing should prevent you from making it work, we just have to figure out what is being generated and how to normalize it all to the right type declarations.
I updated my comment with further information.
My last try was the right one, simply I forgot the @Inject annotation on the constructor of ExampleImpl.
The following summary is for people who encounter the same problem.
Thanks for your help, considering that now it works, I close the issue, feel free to re-open it if needed.
Interface
interface Example<T>
Implementation
class ExampleImpl @Inject constructor() : Example<List<@JvmSuppressWildcards String>>
Module
@Module
interface ExampleModule {
@Binds
fun provideExample(example: ExampleImpl): Example<List<String>>
}
Consumer
class Consumer @Inject constructor(private val example: @JvmSuppressWildcards Example<List<String>>)
Closing this since it looks like https://github.com/google/dagger/issues/1143#issuecomment-381776533 has the solution.
Most helpful comment
My last try was the right one, simply I forgot the
@Injectannotation on the constructor ofExampleImpl.The following summary is for people who encounter the same problem.
Thanks for your help, considering that now it works, I close the issue, feel free to re-open it if needed.
Short summary
Interface
Implementation
Module
Consumer