Dagger doesn't recognize one provided method in Kotlin. This is the important part of the module:
@Provides
@AppScope
fun provideClient(cache: Cache, interceptors: List<Interceptor>?): OkHttpClient {
val httpBuilder = OkHttpClient.Builder()
interceptors?.let {
for (interceptor in interceptors) {
httpBuilder.addInterceptor(interceptor)
}
}
return httpBuilder
.cache(cache)
.build()
}
@Provides
@AppScope
fun provideInterceptors(): List<Interceptor>? {
return listOf(HttpLoggingInterceptor().setLevel(WebServiceConfig.LOGGING_LEVEL))
}
The error message is as follows:
AppComponent.java:15: error: java.util.List<? extends okhttp3.Interceptor> cannot be provided without an @Provides-annotated method.
If I use MutableList, then it works. Therefore the question is: what is the problem with List in Dagger2 / Kotlin?
Turns out this is a generics interop issue.
When you use a List of an interface (like Interceptor) as a parameter in Kotlin, you see it as having a wildcard for the type parameter of the list from Java's point of view, because List is covariant:
OkHttpClient provideClient(List<? extends Interceptor> interceptors) { ... }
However, this wildcard doesn't get added for return types.
List<Interceptor> provideInterceptors() { ... }
You can check this by creating an instance of your module in a Java file, and looking at the methods offered by the autocompletion.
So the problem is that Dagger is looking for a List<? extends Interceptor> while your other method is returning a List<Interceptor>.
Possible solutions:
Use the @JvmSuppressWildCards annotation to prevent the wildcard from being added (see a related question here). This can be used in just about any scope, all the way from the entire module to only on the single type parameter you're having an issue with:
interceptors: List<@JvmSuppressWildcards Interceptor>?
Add explicit out variance on the List that you're returning in the provideInterceptors method. Interestingly, this doesn't show when you look at autocompletion from Java, but it fixes the build.
fun provideInterceptors(): List<out Interceptor>? { ... }
Use the MutableList interface, which as you've discovered, doesn't have this issue.
As for why this only happens when you use a List and not a MutableList: List only has its type parameter in out positions, and therefore, it's covariant. This causes the the wildcard to be generated for a List but not for the invariant MutableList (which is why that works fine).
Also note that this wildcard generation only happens when the type parameter is a non-final type (an open class or an interface). So you wouldn't get this issue for, say a List<StringBuilder> (which is final), but you'd get it for a
List<BufferedReader> (which isn't).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With