or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdmap-binding.mdoptional-binding.mdset-binding.md

map-binding.mddocs/

0

# Map Binding (MapBinder)

1

2

Map binding functionality that allows multiple modules to contribute key-value entries to a single Map collection. Entries are bound individually and then injected as a complete Map, enabling registry-style patterns where different modules can register services, configurations, or handlers by name.

3

4

## Capabilities

5

6

### MapBinder Factory Methods

7

8

Creates new MapBinder instances for different key/value type and annotation combinations.

9

10

```java { .api }

11

/**

12

* Returns a new mapbinder that collects entries of keyType/valueType in a Map

13

* that is itself bound with no binding annotation.

14

*/

15

public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType);

16

17

/**

18

* Returns a new mapbinder that collects entries of keyType/valueType in a Map

19

* that is itself bound with no binding annotation.

20

*/

21

public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType);

22

23

/**

24

* Returns a new mapbinder that collects entries of keyType/valueType in a Map

25

* that is itself bound with annotation.

26

*/

27

public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, Annotation annotation);

28

29

/**

30

* Returns a new mapbinder that collects entries of keyType/valueType in a Map

31

* that is itself bound with annotation.

32

*/

33

public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Annotation annotation);

34

35

/**

36

* Returns a new mapbinder that collects entries of keyType/valueType in a Map

37

* that is itself bound with annotationType.

38

*/

39

public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, Class<? extends Annotation> annotationType);

40

41

/**

42

* Returns a new mapbinder that collects entries of keyType/valueType in a Map

43

* that is itself bound with annotationType.

44

*/

45

public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Class<? extends Annotation> annotationType);

46

```

47

48

### Entry Binding

49

50

Add key-value entries to the Map collection.

51

52

```java { .api }

53

/**

54

* Returns a binding builder used to add a new entry in the map. Each key must be

55

* distinct (and non-null). Bound providers will be evaluated each time the map is injected.

56

*

57

* It is an error to call this method without also calling one of the to methods on the

58

* returned binding builder. Scoping elements independently is supported.

59

*/

60

public LinkedBindingBuilder<V> addBinding(K key);

61

```

62

63

### Duplicate Key Handling

64

65

Configure how duplicate keys are handled.

66

67

```java { .api }

68

/**

69

* Configures the MapBinder to handle duplicate entries.

70

*

71

* When multiple equal keys are bound, the value that gets included in the map is arbitrary.

72

* In addition to the Map<K, V> and Map<K, Provider<V>> maps that are normally bound,

73

* a Map<K, Set<V>> and Map<K, Set<Provider<V>>> are also bound, which contain all values

74

* bound to each key.

75

*

76

* When multiple modules contribute elements to the map, this configuration option impacts all of them.

77

*/

78

public MapBinder<K, V> permitDuplicates();

79

```

80

81

### Provider Method Support

82

83

Contribute entries using provider methods with map-specific annotations.

84

85

```java { .api }

86

/**

87

* Annotates methods of a Module to add items to a MapBinder. The method's return

88

* type, binding annotation and additional key annotation determines what Map this will contribute to.

89

*/

90

@Target(METHOD)

91

@Retention(RUNTIME)

92

public @interface ProvidesIntoMap {}

93

94

/**

95

* Meta-annotation for creating custom map key annotations. When unwrapValue is true,

96

* the value() type will be the key type for injected map and the value() instances

97

* will be the keys values.

98

*/

99

@Target(ANNOTATION_TYPE)

100

@Retention(RUNTIME)

101

public @interface MapKey {

102

boolean unwrapValue() default true;

103

}

104

105

/**

106

* Built-in map key annotation for String keys.

107

*/

108

@MapKey(unwrapValue = true)

109

@Target(METHOD)

110

@Retention(RUNTIME)

111

public @interface StringMapKey {

112

String value();

113

}

114

115

/**

116

* Built-in map key annotation for Class keys.

117

*/

118

@MapKey(unwrapValue = true)

119

@Target(METHOD)

120

@Retention(RUNTIME)

121

public @interface ClassMapKey {

122

Class<?> value();

123

}

124

```

125

126

**Usage Examples:**

127

128

**Basic Map Binding:**

129

130

```java

131

public class SnacksModule extends AbstractModule {

132

@Override

133

protected void configure() {

134

MapBinder<String, Snack> mapbinder =

135

MapBinder.newMapBinder(binder(), String.class, Snack.class);

136

mapbinder.addBinding("twix").toInstance(new Twix());

137

mapbinder.addBinding("snickers").toProvider(SnickersProvider.class);

138

mapbinder.addBinding("skittles").to(Skittles.class);

139

}

140

}

141

142

// Injection

143

@Inject

144

public SnackMachine(Map<String, Snack> snacks) {

145

this.snacks = snacks; // Contains {"twix" -> Twix, "snickers" -> Snickers, "skittles" -> Skittles}

146

}

147

```

148

149

**Provider Map Injection:**

150

151

```java

152

// Can inject Map<K, Provider<V>> for lazy value evaluation

153

@Inject

154

public SnackMachine(Map<String, Provider<Snack>> snackProviders) {

155

this.snackProviders = snackProviders;

156

// Values are created only when Provider.get() is called

157

}

158

```

159

160

**Annotated Map Binding:**

161

162

```java

163

public class HandlersModule extends AbstractModule {

164

@Override

165

protected void configure() {

166

MapBinder<String, RequestHandler> mapbinder = MapBinder.newMapBinder(

167

binder(), String.class, RequestHandler.class, Names.named("web"));

168

mapbinder.addBinding("users").to(UserRequestHandler.class);

169

mapbinder.addBinding("orders").to(OrderRequestHandler.class);

170

}

171

}

172

173

// Injection

174

@Inject

175

public WebServer(@Named("web") Map<String, RequestHandler> handlers) {

176

this.handlers = handlers;

177

}

178

```

179

180

**Provider Method Binding with String Keys:**

181

182

```java

183

public class PluginsModule extends AbstractModule {

184

@ProvidesIntoMap

185

@StringMapKey("user")

186

Plugin provideUserPlugin(UserService userService) {

187

return new UserPlugin(userService);

188

}

189

190

@ProvidesIntoMap

191

@StringMapKey("order")

192

Plugin provideOrderPlugin() {

193

return new OrderPlugin();

194

}

195

196

@ProvidesIntoMap

197

@StringMapKey("notification")

198

@Named("web") // Can combine with binding annotations

199

Plugin provideWebNotificationPlugin() {

200

return new WebNotificationPlugin();

201

}

202

}

203

```

204

205

**Provider Method Binding with Class Keys:**

206

207

```java

208

public class ProcessorsModule extends AbstractModule {

209

@ProvidesIntoMap

210

@ClassMapKey(String.class)

211

Processor provideStringProcessor() {

212

return new StringProcessor();

213

}

214

215

@ProvidesIntoMap

216

@ClassMapKey(Integer.class)

217

Processor provideIntegerProcessor() {

218

return new IntegerProcessor();

219

}

220

}

221

222

// Injection

223

@Inject

224

public ProcessorRegistry(Map<Class<?>, Processor> processors) {

225

this.processors = processors; // {String.class -> StringProcessor, Integer.class -> IntegerProcessor}

226

}

227

```

228

229

**Custom Map Key Annotation:**

230

231

```java

232

// Define custom enum key annotation

233

public enum Environment { DEV, TEST, PROD }

234

235

@MapKey(unwrapValue = true)

236

@Retention(RUNTIME)

237

public @interface EnvironmentKey {

238

Environment value();

239

}

240

241

// Use in provider method

242

@ProvidesIntoMap

243

@EnvironmentKey(Environment.PROD)

244

DatabaseConfig provideProdConfig() {

245

return new ProductionDatabaseConfig();

246

}

247

```

248

249

**Multiple Modules Contributing:**

250

251

```java

252

// CandyModule

253

public class CandyModule extends AbstractModule {

254

@Override

255

protected void configure() {

256

MapBinder<String, Snack> mapbinder =

257

MapBinder.newMapBinder(binder(), String.class, Snack.class);

258

mapbinder.addBinding("chocolate").to(Chocolate.class);

259

mapbinder.addBinding("gummies").to(Gummies.class);

260

}

261

}

262

263

// ChipsModule

264

public class ChipsModule extends AbstractModule {

265

@Override

266

protected void configure() {

267

MapBinder<String, Snack> mapbinder =

268

MapBinder.newMapBinder(binder(), String.class, Snack.class);

269

mapbinder.addBinding("doritos").to(Doritos.class);

270

mapbinder.addBinding("cheetos").to(Cheetos.class);

271

}

272

}

273

274

// Final injected Map contains entries from both modules

275

```

276

277

**Duplicate Key Handling:**

278

279

```java

280

public class ConfigModule extends AbstractModule {

281

@Override

282

protected void configure() {

283

MapBinder<String, String> mapbinder = MapBinder.newMapBinder(binder(), String.class, String.class)

284

.permitDuplicates(); // Enable duplicate key support

285

mapbinder.addBinding("env").toInstance("dev");

286

mapbinder.addBinding("env").toInstance("test"); // Would normally fail without permitDuplicates()

287

}

288

}

289

290

// With permitDuplicates(), additional Map types are bound:

291

@Inject Map<String, Set<String>> multiValueMap; // {"env" -> {"dev", "test"}}

292

@Inject Map<String, Set<Provider<String>>> multiProviderMap;

293

```

294

295

## Key Features

296

297

- **Key Uniqueness**: By default, keys must be distinct or injection will fail

298

- **Iteration Order**: Map iteration order is consistent with binding order within a module

299

- **Multi-Module Support**: Multiple modules can contribute to the same Map

300

- **Provider Support**: Can inject `Map<K, Provider<V>>` for lazy value evaluation

301

- **Scoping**: Individual values can be scoped independently

302

- **Annotation Support**: Different Maps of same key/value type can be created using binding annotations

303

- **Duplicate Key Support**: When enabled, provides additional `Map<K, Set<V>>` bindings

304

- **Custom Key Types**: Extensible key annotation system supports any serializable type