com.google.inject.multibindings.Multibinder Maven / Gradle / Ivy
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.inject.multibindings;
import static com.google.inject.internal.RealMultibinder.newRealSetBinder;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.RealMultibinder;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Set;
/**
* An API to bind multiple values separately, only to later inject them as a complete collection.
* Multibinder is intended for use in your application's module:
*
*
* public class SnacksModule extends AbstractModule {
* protected void configure() {
* Multibinder<Snack> multibinder
* = Multibinder.newSetBinder(binder(), Snack.class);
* multibinder.addBinding().toInstance(new Twix());
* multibinder.addBinding().toProvider(SnickersProvider.class);
* multibinder.addBinding().to(Skittles.class);
* }
* }
*
* With this binding, a {@link Set}{@code } can now be injected:
*
*
* class SnackMachine {
* {@literal @}Inject
* public SnackMachine(Set<Snack> snacks) { ... }
* }
*
* If desired, {@link Collection}{@code >} can also be injected.
*
* Contributing multibindings from different modules is supported. For example, it is okay for
* both {@code CandyModule} and {@code ChipsModule} to create their own {@code Multibinder},
* and to each contribute bindings to the set of snacks. When that set is injected, it will contain
* elements from both modules.
*
* The set's iteration order is consistent with the binding order. This is convenient when
* multiple elements are contributed by the same module because that module can order its bindings
* appropriately. Avoid relying on the iteration order of elements contributed by different modules,
* since there is no equivalent mechanism to order modules.
*
*
The set is unmodifiable. Elements can only be added to the set by configuring the multibinder.
* Elements can never be removed from the set.
*
*
Elements are resolved at set injection time. If an element is bound to a provider, that
* provider's get method will be called each time the set is injected (unless the binding is also
* scoped).
*
*
Annotations are be used to create different sets of the same element type. Each distinct
* annotation gets its own independent collection of elements.
*
*
Elements must be distinct. If multiple bound elements have the same value,
* set injection will fail.
*
*
Elements must be non-null. If any set element is null, set injection will
* fail.
*
* @author [email protected] (Jesse Wilson)
*/
public class Multibinder {
// This class is non-final due to users mocking this in tests :(
/**
* Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
* itself bound with no binding annotation.
*/
public static Multibinder newSetBinder(Binder binder, TypeLiteral type) {
return newSetBinder(binder, Key.get(type));
}
/**
* Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
* itself bound with no binding annotation.
*/
public static Multibinder newSetBinder(Binder binder, Class type) {
return newSetBinder(binder, Key.get(type));
}
/**
* Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
* itself bound with {@code annotation}.
*/
public static Multibinder newSetBinder(
Binder binder, TypeLiteral type, Annotation annotation) {
return newSetBinder(binder, Key.get(type, annotation));
}
/**
* Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
* itself bound with {@code annotation}.
*/
public static Multibinder newSetBinder(
Binder binder, Class type, Annotation annotation) {
return newSetBinder(binder, Key.get(type, annotation));
}
/**
* Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
* itself bound with {@code annotationType}.
*/
public static Multibinder newSetBinder(
Binder binder, TypeLiteral type, Class extends Annotation> annotationType) {
return newSetBinder(binder, Key.get(type, annotationType));
}
/**
* Returns a new multibinder that collects instances of the key's type in a {@link Set} that is
* itself bound with the annotation (if any) of the key.
*
* @since 4.0
*/
public static Multibinder newSetBinder(Binder binder, Key key) {
return new Multibinder(newRealSetBinder(binder.skipSources(Multibinder.class), key));
}
/**
* Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
* itself bound with {@code annotationType}.
*/
public static Multibinder newSetBinder(
Binder binder, Class type, Class extends Annotation> annotationType) {
return newSetBinder(binder, Key.get(type, annotationType));
}
private final RealMultibinder delegate;
private Multibinder(RealMultibinder delegate) {
this.delegate = delegate;
}
/**
* Configures the bound set to silently discard duplicate elements. When multiple equal values are
* bound, the one that gets included is arbitrary. When multiple modules contribute elements to
* the set, this configuration option impacts all of them.
*
* @return this multibinder
* @since 3.0
*/
public Multibinder permitDuplicates() {
delegate.permitDuplicates();
return this;
}
/**
* Returns a binding builder used to add a new element in the set. Each bound element must have a
* distinct value. Bound providers will be evaluated each time the set is injected.
*
* It is an error to call this method without also calling one of the {@code to} methods on the
* returned binding builder.
*
*
Scoping elements independently is supported. Use the {@code in} method to specify a binding
* scope.
*/
public LinkedBindingBuilder addBinding() {
return delegate.addBinding();
}
// Some tests rely on Multibinder implementing equals/hashCode
@Override
public boolean equals(Object obj) {
if (obj instanceof Multibinder) {
return delegate.equals(((Multibinder>) obj).delegate);
}
return false;
}
@Override
public int hashCode() {
return delegate.hashCode();
}
}