Skip to content

Multibinding

Table of Contents

Overview

Use @AutoBindsIntoSet to contribute a class into a Dagger multibinding Set of each direct supertype (implemented interfaces and extended parent classes). This lets you collect multiple implementations automatically without maintaining a manual module.

Contributing to a Set

Annotate each implementation with @AutoBindsIntoSet:

@AutoBindsIntoSet
class LoggingInterceptor @Inject constructor() : Interceptor {
    override fun intercept(chain: Chain): Response { /* ... */ }
}

@AutoBindsIntoSet
class AuthInterceptor @Inject constructor() : Interceptor {
    override fun intercept(chain: Chain): Response { /* ... */ }
}

Now you can inject Set<Interceptor> anywhere and Hilt will collect all contributions automatically:

class ApiClient @Inject constructor(
    private val interceptors: Set<@JvmSuppressWildcards Interceptor>,
) {
    // interceptors contains LoggingInterceptor and AuthInterceptor
}

Combining with @AutoBinds

@AutoBindsIntoSet can be combined with @AutoBinds on the same class to produce both a direct binding and a set contribution:

@AutoBinds            // provides Interceptor directly
@AutoBindsIntoSet     // also contributes to Set<Interceptor>
class DefaultInterceptor @Inject constructor() : Interceptor { /* ... */ }

This generates two separate modules with different names to avoid conflicts.

Choosing a Component

@AutoBindsIntoSet supports the same installIn parameter as @AutoBinds. It defaults to HiltComponent.Unspecified with auto-detection from scope annotations:

@ActivityScoped
@AutoBindsIntoSet  // installed in ActivityComponent (auto-detected)
class ActivityInterceptor @Inject constructor() : Interceptor { /* ... */ }

@AutoBindsIntoSet(installIn = HiltComponent.ViewModel)
class ViewModelInterceptor @Inject constructor() : Interceptor { /* ... */ }

See Scopes and Components for the full reference.

Selecting Specific Binding Targets

By default, @AutoBindsIntoSet contributes the class to a Set of every direct supertype. Use bindTo to restrict which supertypes are included or to target a grandparent:

interface BaseHandler
open class AbstractHandler : BaseHandler

// Contributes to Set<BaseHandler> only, skipping AbstractHandler
@AutoBindsIntoSet(bindTo = [BaseHandler::class])
class MyHandler @Inject constructor() : AbstractHandler()

bindTo accepts any transitive supertype. An error is emitted at compile time if a listed type is not a supertype of the annotated class.

Generated Code

For each annotated class, the processor generates an interface Hilt module with @Binds @IntoSet functions:

@Module
@InstallIn(SingletonComponent::class)
internal interface LoggingInterceptor__IntoSetModule {
    @Binds @IntoSet
    fun bindToInterceptor(impl: LoggingInterceptor): Interceptor
}

The same class requirements as basic usage apply: the class must be concrete, non-abstract, not an inner class, have @Inject on its primary constructor, and implement at least one interface or extend a parent class.