0
# Multibindings
1
2
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.
3
4
## Capabilities
5
6
### Set Multibindings
7
8
#### @IntoSet Annotation
9
10
Method return type contributes individual elements to a Set<T>. The final injected set is immutable and contains all contributed elements.
11
12
```java { .api }
13
/**
14
* Annotates provider methods whose return type contributes to a Set<T>
15
*/
16
@Target(ElementType.METHOD)
17
@Retention(RetentionPolicy.RUNTIME)
18
@interface IntoSet {}
19
```
20
21
**Usage Examples:**
22
23
```java
24
@Module
25
public class ValidationModule {
26
@Provides
27
@IntoSet
28
Validator provideEmailValidator() {
29
return new EmailValidator();
30
}
31
32
@Provides
33
@IntoSet
34
Validator providePhoneValidator() {
35
return new PhoneValidator();
36
}
37
}
38
39
@Module
40
public class AdditionalValidationModule {
41
@Provides
42
@IntoSet
43
Validator provideCreditCardValidator() {
44
return new CreditCardValidator();
45
}
46
}
47
48
// Injection
49
public class ValidationService {
50
private final Set<Validator> validators;
51
52
@Inject
53
public ValidationService(Set<Validator> validators) {
54
this.validators = validators; // Contains all 3 validators
55
}
56
}
57
```
58
59
#### @ElementsIntoSet Annotation
60
61
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.
62
63
```java { .api }
64
/**
65
* Annotates provider methods whose return type is Set<T>, contributing all elements
66
*/
67
@Target(ElementType.METHOD)
68
@Retention(RetentionPolicy.RUNTIME)
69
@interface ElementsIntoSet {}
70
```
71
72
**Usage Examples:**
73
74
```java
75
@Module
76
public class PluginModule {
77
@Provides
78
@ElementsIntoSet
79
Set<Plugin> provideDefaultPlugins() {
80
return ImmutableSet.of(
81
new LoggingPlugin(),
82
new MetricsPlugin(),
83
new CachePlugin()
84
);
85
}
86
87
@Provides
88
@ElementsIntoSet
89
Set<Plugin> provideConditionalPlugins() {
90
if (BuildConfig.DEBUG) {
91
return ImmutableSet.of(new DebugPlugin());
92
}
93
return Collections.emptySet();
94
}
95
}
96
```
97
98
#### @Multibinds Annotation
99
100
Declares that a multibinding exists, ensuring an empty collection is available when no contributions are made. Required for sets/maps that may be empty.
101
102
```java { .api }
103
/**
104
* Annotates abstract methods that declare multibindings exist
105
*/
106
@Target(ElementType.METHOD)
107
@Retention(RetentionPolicy.RUNTIME)
108
@interface Multibinds {}
109
```
110
111
**Usage Examples:**
112
113
```java
114
@Module
115
public abstract class PluginDeclarationModule {
116
// Ensures Set<Plugin> is available even if no plugins contribute
117
@Multibinds
118
abstract Set<Plugin> declarePluginSet();
119
120
// Ensures Map<String, Service> is available even if empty
121
@Multibinds
122
abstract Map<String, Service> declareServiceMap();
123
}
124
```
125
126
### Map Multibindings
127
128
#### @IntoMap Annotation
129
130
Method return type contributes to a Map<K, Provider<V>>. Must be combined with a @MapKey-annotated annotation to specify the key.
131
132
```java { .api }
133
/**
134
* Annotates provider methods whose return type contributes to a Map<K, Provider<V>>
135
*/
136
@Target(ElementType.METHOD)
137
@Retention(RetentionPolicy.RUNTIME)
138
@interface IntoMap {}
139
```
140
141
**Usage Examples:**
142
143
```java
144
@Module
145
public class ServiceModule {
146
@Provides
147
@IntoMap
148
@StringKey("user")
149
Service provideUserService() {
150
return new UserService();
151
}
152
153
@Provides
154
@IntoMap
155
@StringKey("order")
156
Service provideOrderService() {
157
return new OrderService();
158
}
159
}
160
161
// Injection - note Provider<V> values
162
public class ServiceRegistry {
163
private final Map<String, Provider<Service>> services;
164
165
@Inject
166
public ServiceRegistry(Map<String, Provider<Service>> services) {
167
this.services = services;
168
}
169
170
public Service getService(String name) {
171
Provider<Service> provider = services.get(name);
172
return provider != null ? provider.get() : null;
173
}
174
}
175
```
176
177
### Map Key Annotations
178
179
#### @MapKey Meta-Annotation
180
181
Identifies annotation types used to associate keys with values for map multibindings.
182
183
```java { .api }
184
/**
185
* Meta-annotation for creating map key annotations
186
*/
187
@Target(ElementType.ANNOTATION_TYPE)
188
@Retention(RetentionPolicy.RUNTIME)
189
@interface MapKey {
190
/**
191
* If true, use the annotation member value as the key
192
* If false, use the entire annotation instance as the key
193
*/
194
boolean unwrapValue() default true;
195
}
196
```
197
198
#### Standard Map Key Annotations
199
200
**@StringKey:**
201
```java { .api }
202
/**
203
* MapKey annotation for String keys
204
*/
205
@MapKey
206
@Target(ElementType.METHOD)
207
@Retention(RetentionPolicy.RUNTIME)
208
@interface StringKey {
209
String value();
210
}
211
```
212
213
**@IntKey:**
214
```java { .api }
215
/**
216
* MapKey annotation for int keys
217
*/
218
@MapKey
219
@Target(ElementType.METHOD)
220
@Retention(RetentionPolicy.RUNTIME)
221
@interface IntKey {
222
int value();
223
}
224
```
225
226
**@LongKey:**
227
```java { .api }
228
/**
229
* MapKey annotation for long keys
230
*/
231
@MapKey
232
@Target(ElementType.METHOD)
233
@Retention(RetentionPolicy.RUNTIME)
234
@interface LongKey {
235
long value();
236
}
237
```
238
239
**@ClassKey:**
240
```java { .api }
241
/**
242
* MapKey annotation for Class<?> keys
243
*/
244
@MapKey
245
@Target(ElementType.METHOD)
246
@Retention(RetentionPolicy.RUNTIME)
247
@interface ClassKey {
248
Class<?> value();
249
}
250
```
251
252
**@LazyClassKey:**
253
```java { .api }
254
/**
255
* MapKey annotation for Class<?> keys with lazy loading to prevent class loading
256
*/
257
@MapKey(unwrapValue = false)
258
@Target(ElementType.METHOD)
259
@Retention(RetentionPolicy.RUNTIME)
260
@interface LazyClassKey {
261
Class<?> value();
262
}
263
```
264
265
### Custom Map Keys
266
267
You can create custom map key annotations for complex keys:
268
269
```java
270
// Simple custom key (unwrapValue = true)
271
@MapKey
272
@Target(ElementType.METHOD)
273
@Retention(RetentionPolicy.RUNTIME)
274
public @interface EnvironmentKey {
275
Environment value();
276
}
277
278
// Complex custom key (unwrapValue = false)
279
@MapKey(unwrapValue = false)
280
@Target(ElementType.METHOD)
281
@Retention(RetentionPolicy.RUNTIME)
282
public @interface ServiceKey {
283
String name();
284
int version();
285
Environment environment();
286
}
287
288
// Usage
289
@Module
290
public class ComplexKeyModule {
291
@Provides
292
@IntoMap
293
@ServiceKey(name = "user", version = 2, environment = Environment.PROD)
294
Service provideUserServiceV2() {
295
return new UserServiceV2();
296
}
297
}
298
```
299
300
### Multibinding with @Binds
301
302
Both @IntoSet and @IntoMap work with @Binds for more efficient delegation:
303
304
```java
305
@Module
306
public abstract class ValidatorBindingModule {
307
@Binds
308
@IntoSet
309
abstract Validator bindEmailValidator(EmailValidator impl);
310
311
@Binds
312
@IntoMap
313
@StringKey("email")
314
abstract Validator bindEmailValidatorToMap(EmailValidator impl);
315
}
316
```
317
318
### Qualified Multibindings
319
320
Multibindings support qualifiers to create separate collections:
321
322
```java
323
@Qualifier
324
@Retention(RetentionPolicy.RUNTIME)
325
public @interface Internal {}
326
327
@Module
328
public class QualifiedMultibindingModule {
329
@Provides
330
@IntoSet
331
@Internal
332
Service provideInternalService() {
333
return new InternalService();
334
}
335
336
@Provides
337
@IntoSet
338
Service providePublicService() {
339
return new PublicService();
340
}
341
}
342
343
// Different sets injected based on qualifier
344
public class ServiceManager {
345
@Inject
346
public ServiceManager(
347
Set<Service> publicServices,
348
@Internal Set<Service> internalServices
349
) {
350
// Two separate sets
351
}
352
}
353
```
354
355
### Multibinding Best Practices
356
357
**Declare Empty Collections:**
358
```java
359
@Module
360
public abstract class PluginDeclarations {
361
@Multibinds abstract Set<Plugin> plugins();
362
@Multibinds abstract Map<String, Handler> handlers();
363
}
364
```
365
366
**Use Appropriate Collection Types:**
367
```java
368
// For individual contributions
369
@IntoSet
370
Validator provideValidator() { /* ... */ }
371
372
// For bulk contributions
373
@ElementsIntoSet
374
Set<Validator> provideValidators() { /* ... */ }
375
```
376
377
**Consider Map vs Set:**
378
```java
379
// Use Set for collections of similar items
380
Set<Validator> validators;
381
382
// Use Map for keyed lookups
383
Map<String, Provider<Service>> servicesByName;
384
```