or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assisted-injection.mdcomponent-framework.mdindex.mdmodule-system.mdmultibindings.mdutility-types.md

multibindings.mddocs/

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

```