Dagger: Can't create Set<Interceptor> using Multibindings

Created on 28 Mar 2017  Â·  12Comments  Â·  Source: google/dagger

Hi!

I trying to use multibinding to provide OkHttp Interceptors for the client.

    @Provides
    @Singleton
    fun provideOkHttpClient(interceptors: Set<Interceptor>): OkHttpClient {
        val builder = OkHttpClient.Builder()
        for (interceptor in interceptors) {
            builder.addNetworkInterceptor(interceptor)
        }
        return builder.build()
    }

    @Provides @IntoSet
    @Singleton
    fun provideLoggingInterceptor(): Interceptor =
            HttpLoggingInterceptor({ message -> Timber.tag("OkHttp").d(message) })

but can't compile. There is an error:

error: java.util.Set<? extends okhttp3.Interceptor> cannot be provided without an @Provides-annotated method.

When I provide strings in the same way all works. But not with the Interceptor.

What I use:
Kotlin 1.1.1 (kapt{generateStubs = true}) ,
Dagger 2.10

Most helpful comment

Dagger expects a java.util.Set<okhttp3.Interceptor>, not ? extends okhttp3.Interceptor. The problem is that you're using Kotlin and Kotlin will by default translate a Set<X> into a Set<? extends X>: http://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#variant-generics

Apparently (according to the docs), you should be able to make it work with Set<@JvmSuppressWildcards okhttp3.Interceptor>.

Wrt Set<String>, it works because String is final, so Kotlin emits a Set<String> as Set<? extends String> would have no sense. See this note in the docs:

NOTE: when the argument type is final, there's usually no point in generating the wildcard, so Box<String> is always Box<String>, no matter what position it takes.

All 12 comments

Here is full stacktrace if it can help:

error: java.util.Set<? extends okhttp3.Interceptor> cannot be provided without an @Provides-annotated method.
      java.util.Set<? extends okhttp3.Interceptor> is injected at
          ru.mobileup.igooods.network.NetworkModule.provideOkHttpClient(arg0)
      okhttp3.OkHttpClient is injected at
          ru.mobileup.igooods.network.NetworkModule.provideRetrofit(arg0, …)
      retrofit2.Retrofit is injected at
          ru.mobileup.igooods.network.NetworkModule.provideApi(arg0, …)
      ru.mobileup.igooods.network.IgooodsApi is injected at
          ru.mobileup.igooods.gateway.GatewayModule.provideAuthGateway(arg0, …)
      ru.mobileup.igooods.gateway.AuthGateway is injected at
          ru.mobileup.igooods.ui.main.MainPresenter.authGateway
      ru.mobileup.igooods.ui.main.MainPresenter is injected at
          ru.mobileup.igooods.AppComponent.inject(arg0)
1 error
:app:compileStubDebugJavaWithJavac FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:compileStubDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:compileStubDebugJavaWithJavac'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:84)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:55)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:62)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
        at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:51)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:236)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:228)
        at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:61)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:228)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:215)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:77)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:58)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:32)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:113)
        at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
        at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
        at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
        at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
        at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
        at org.gradle.initialization.DefaultGradleLauncher$3.execute(DefaultGradleLauncher.java:196)
        at org.gradle.initialization.DefaultGradleLauncher$3.execute(DefaultGradleLauncher.java:193)
        at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:56)
        at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:193)
        at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:119)
        at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:102)
        at org.gradle.launcher.exec.GradleBuildController.run(GradleBuildController.java:71)
        at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
        at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:41)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
        at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:75)
        at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:49)
        at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:44)
        at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:29)
        at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
        at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:47)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
        at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
        at org.gradle.util.Swapper.swap(Swapper.java:38)
        at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
        at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:72)
        at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
        at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
        at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
Caused by: org.gradle.api.internal.tasks.compile.CompilationFailedException: Compilation failed; see the compiler error output for details.
        at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:48)
        at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:33)
        at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.delegateAndHandleErrors(NormalizingJavaCompiler.java:104)
        at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:53)
        at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:38)
        at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:35)
        at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:25)
        at org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilationFinalizer.execute(IncrementalCompilationFinalizer.java:39)
        at org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilationFinalizer.execute(IncrementalCompilationFinalizer.java:24)
        at org.gradle.api.tasks.compile.JavaCompile.performCompilation(JavaCompile.java:206)
        at org.gradle.api.tasks.compile.JavaCompile.compile(JavaCompile.java:164)
        at com.android.build.gradle.tasks.factory.AndroidJavaCompile.compile(AndroidJavaCompile.java:49)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
        at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:163)
        at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
        at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:123)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:95)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:76)
        ... 70 more

Dagger expects a java.util.Set<okhttp3.Interceptor>, not ? extends okhttp3.Interceptor. The problem is that you're using Kotlin and Kotlin will by default translate a Set<X> into a Set<? extends X>: http://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#variant-generics

Apparently (according to the docs), you should be able to make it work with Set<@JvmSuppressWildcards okhttp3.Interceptor>.

Wrt Set<String>, it works because String is final, so Kotlin emits a Set<String> as Set<? extends String> would have no sense. See this note in the docs:

NOTE: when the argument type is final, there's usually no point in generating the wildcard, so Box<String> is always Box<String>, no matter what position it takes.

Thanks for the great explanation!
Now all works.

This keeps coming up. I wonder if we should try to inspect the classpath for kotlin classes (if that's possible?) and present an error when we see this

This keeps coming up. I wonder if we should try to inspect the classpath for kotlin classes (if that's possible?) and present an error when we see this

I contributed this code to Spring that will give you the detection bit:

https://github.com/spring-projects/spring-framework/blob/87598f48e41d483745aba56cbf4e998c6f6d680c/spring-core/src/main/java/org/springframework/core/MethodParameter.java#L62

It might also be worth adding a few kotlin tips and tricks to the dagger docs, since I think there are a small number of super-common interop mis-matches with easy workarounds. Now that I've dug into this issue, having hit it in my own efforts, I see the answer in six places.

Like, @tbroyer's comment above is basically nearly the perfect FAQ answer. :)

We could have a dagger-kotlin annotation processor that recommended that people use @JvmSuppressWildcards at injection sites. That might be annoying if someone did actually want to inject a wildcard-extending set though.

If something cannot be provided, and it has wildcard type arguments, and it could be provided without wildcards (could be complex depending on number of type arguments), and there's kotlin in the compile classpath, then suggest to add the annotation. WDYT?

I wish the link to this issue was added to the error message 🗡

@tbroyer I am still facing this isssue.
[Dagger/MissingBinding] @com.coders.currencytracker.di.qualifiers.AppInterceptor java.util.Set cannot be provided without an @Provides-annotated method.

@Provides
@NetworkInterceptor
@IntoSet
@Singleton
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor { message -> Log.d("OkHttp", message) }
.setLevel(HttpLoggingInterceptor.Level.BODY)
}

@Provides
@NetworkInterceptor
@IntoSet
@Singleton
fun provideStethoInterceptor(): StethoInterceptor {
    return StethoInterceptor()
}

@Provides
@AppInterceptor
@IntoSet
@Singleton
fun provideChuckInterceptor(@ForApplication context: Context): ChuckInterceptor {
    return ChuckInterceptor(context)
}

@Provides
@Singleton
fun provideApiOkHttpClient(
@AppInterceptor appInterceptor: Set<@JvmSuppressWildcards Interceptor>,
@NetworkInterceptor networkInterceptor: Set<@JvmSuppressWildcards Interceptor>
): OkHttpClient {
val okBuilder = OkHttpClient.Builder()
okBuilder.interceptors().addAll(appInterceptor)
okBuilder.networkInterceptors().addAll(networkInterceptor)

    return okBuilder.build()
}

You made one set of HttpLoggingInterceptor, another of StethoInterceptor and a third of ChuckInterceptor (with qualifiers). Change your return types to Interceptor.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Axrorxoja picture Axrorxoja  Â·  3Comments

SAGARSURI picture SAGARSURI  Â·  3Comments

mskx42 picture mskx42  Â·  3Comments

sagarwaghmare07 picture sagarwaghmare07  Â·  3Comments

matpag picture matpag  Â·  3Comments