CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-dagger--dagger

A fast dependency injector for Java and Android that generates plain Java source code without using reflection or runtime bytecode generation

Pending
Overview
Eval results
Files

multibindings.mddocs/

Multibindings

Multibindings in Dagger allow multiple modules to contribute elements to the same collection (Set or Map), enabling modular composition of related dependencies. This pattern is particularly useful for plugin systems, event handlers, and configurable services.

Capabilities

Set Multibindings

@IntoSet Annotation

Method return type contributes individual elements to a Set<T>. The final injected set is immutable and contains all contributed elements.

/**
 * Annotates provider methods whose return type contributes to a Set<T>
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntoSet {}

Usage Examples:

@Module
public class ValidationModule {
    @Provides
    @IntoSet
    Validator provideEmailValidator() {
        return new EmailValidator();
    }
    
    @Provides
    @IntoSet  
    Validator providePhoneValidator() {
        return new PhoneValidator();
    }
}

@Module
public class AdditionalValidationModule {
    @Provides
    @IntoSet
    Validator provideCreditCardValidator() {
        return new CreditCardValidator();
    }
}

// Injection
public class ValidationService {
    private final Set<Validator> validators;
    
    @Inject
    public ValidationService(Set<Validator> validators) {
        this.validators = validators; // Contains all 3 validators
    }
}

@ElementsIntoSet Annotation

Method return type is Set<T>, and all elements from the returned set are contributed to the final set. Useful for providing default empty sets or bulk contributions.

/**
 * Annotates provider methods whose return type is Set<T>, contributing all elements
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ElementsIntoSet {}

Usage Examples:

@Module
public class PluginModule {
    @Provides
    @ElementsIntoSet
    Set<Plugin> provideDefaultPlugins() {
        return ImmutableSet.of(
            new LoggingPlugin(),
            new MetricsPlugin(),
            new CachePlugin()
        );
    }
    
    @Provides
    @ElementsIntoSet
    Set<Plugin> provideConditionalPlugins() {
        if (BuildConfig.DEBUG) {
            return ImmutableSet.of(new DebugPlugin());
        }
        return Collections.emptySet();
    }
}

@Multibinds Annotation

Declares that a multibinding exists, ensuring an empty collection is available when no contributions are made. Required for sets/maps that may be empty.

/**
 * Annotates abstract methods that declare multibindings exist
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Multibinds {}

Usage Examples:

@Module
public abstract class PluginDeclarationModule {
    // Ensures Set<Plugin> is available even if no plugins contribute
    @Multibinds
    abstract Set<Plugin> declarePluginSet();
    
    // Ensures Map<String, Service> is available even if empty
    @Multibinds
    abstract Map<String, Service> declareServiceMap();
}

Map Multibindings

@IntoMap Annotation

Method return type contributes to a Map<K, Provider<V>>. Must be combined with a @MapKey-annotated annotation to specify the key.

/**
 * Annotates provider methods whose return type contributes to a Map<K, Provider<V>>
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntoMap {}

Usage Examples:

@Module
public class ServiceModule {
    @Provides
    @IntoMap
    @StringKey("user")
    Service provideUserService() {
        return new UserService();
    }
    
    @Provides
    @IntoMap
    @StringKey("order")
    Service provideOrderService() {
        return new OrderService();
    }
}

// Injection - note Provider<V> values
public class ServiceRegistry {
    private final Map<String, Provider<Service>> services;
    
    @Inject
    public ServiceRegistry(Map<String, Provider<Service>> services) {
        this.services = services;
    }
    
    public Service getService(String name) {
        Provider<Service> provider = services.get(name);
        return provider != null ? provider.get() : null;
    }
}

Map Key Annotations

@MapKey Meta-Annotation

Identifies annotation types used to associate keys with values for map multibindings.

/**
 * Meta-annotation for creating map key annotations
 */
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MapKey {
    /**
     * If true, use the annotation member value as the key
     * If false, use the entire annotation instance as the key
     */
    boolean unwrapValue() default true;
}

Standard Map Key Annotations

@StringKey:

/**
 * MapKey annotation for String keys
 */
@MapKey
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface StringKey {
    String value();
}

@IntKey:

/**
 * MapKey annotation for int keys
 */
@MapKey
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntKey {
    int value();
}

@LongKey:

/**
 * MapKey annotation for long keys
 */
@MapKey
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface LongKey {
    long value();
}

@ClassKey:

/**
 * MapKey annotation for Class<?> keys
 */
@MapKey
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassKey {
    Class<?> value();
}

@LazyClassKey:

/**
 * MapKey annotation for Class<?> keys with lazy loading to prevent class loading
 */
@MapKey(unwrapValue = false)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface LazyClassKey {
    Class<?> value();
}

Custom Map Keys

You can create custom map key annotations for complex keys:

// Simple custom key (unwrapValue = true)
@MapKey
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnvironmentKey {
    Environment value();
}

// Complex custom key (unwrapValue = false)
@MapKey(unwrapValue = false)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceKey {
    String name();
    int version();
    Environment environment();
}

// Usage
@Module
public class ComplexKeyModule {
    @Provides
    @IntoMap
    @ServiceKey(name = "user", version = 2, environment = Environment.PROD)
    Service provideUserServiceV2() {
        return new UserServiceV2();
    }
}

Multibinding with @Binds

Both @IntoSet and @IntoMap work with @Binds for more efficient delegation:

@Module
public abstract class ValidatorBindingModule {
    @Binds
    @IntoSet
    abstract Validator bindEmailValidator(EmailValidator impl);
    
    @Binds
    @IntoMap
    @StringKey("email")
    abstract Validator bindEmailValidatorToMap(EmailValidator impl);
}

Qualified Multibindings

Multibindings support qualifiers to create separate collections:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Internal {}

@Module
public class QualifiedMultibindingModule {
    @Provides
    @IntoSet
    @Internal
    Service provideInternalService() {
        return new InternalService();
    }
    
    @Provides
    @IntoSet
    Service providePublicService() {
        return new PublicService();
    }
}

// Different sets injected based on qualifier
public class ServiceManager {
    @Inject
    public ServiceManager(
        Set<Service> publicServices,
        @Internal Set<Service> internalServices
    ) {
        // Two separate sets
    }
}

Multibinding Best Practices

Declare Empty Collections:

@Module
public abstract class PluginDeclarations {
    @Multibinds abstract Set<Plugin> plugins();
    @Multibinds abstract Map<String, Handler> handlers();
}

Use Appropriate Collection Types:

// For individual contributions
@IntoSet
Validator provideValidator() { /* ... */ }

// For bulk contributions  
@ElementsIntoSet
Set<Validator> provideValidators() { /* ... */ }

Consider Map vs Set:

// Use Set for collections of similar items
Set<Validator> validators;

// Use Map for keyed lookups
Map<String, Provider<Service>> servicesByName;

Install with Tessl CLI

npx tessl i tessl/maven-com-google-dagger--dagger

docs

assisted-injection.md

component-framework.md

index.md

module-system.md

multibindings.md

utility-types.md

tile.json