Applying ilya.firman's patch for issue 218. I tweaked the patch to use TypeLiteral's new type resolution methods like TypeLiteral.getReturnType(Method) etc., which makes it so we always just do-the-right-thing for matching type variables.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@871 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
index 4976bd0..b6024e2 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
@@ -17,6 +17,8 @@
package com.google.inject.assistedinject;
import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.Lists;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -41,18 +43,17 @@
private final List<Parameter> allParameters;
@SuppressWarnings("unchecked")
- public AssistedConstructor(Constructor<T> constructor) {
+ public AssistedConstructor(Constructor<T> constructor, List<TypeLiteral<?>> parameterTypes) {
this.constructor = constructor;
- Type[] parameterTypes = constructor.getGenericParameterTypes();
Annotation[][] annotations = constructor.getParameterAnnotations();
- List<Type> typeList = new ArrayList<Type>();
+ List<Type> typeList = Lists.newArrayList();
allParameters = new ArrayList<Parameter>();
// categorize params as @Assisted or @Injected
- for (int i = 0; i < parameterTypes.length; i++) {
- Parameter parameter = new Parameter(parameterTypes[i], annotations[i]);
+ for (int i = 0; i < parameterTypes.size(); i++) {
+ Parameter parameter = new Parameter(parameterTypes.get(i).getType(), annotations[i]);
allParameters.add(parameter);
if (parameter.isProvidedByFactory()) {
typeList.add(parameter.getType());
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
index 3e4c46a..2649188 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
@@ -21,6 +21,7 @@
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ImmutableMap;
import com.google.inject.internal.ImmutableSet;
@@ -34,6 +35,7 @@
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -144,11 +146,16 @@
private Injector injector;
- private final Class<F> factoryType;
+ private final TypeLiteral<F> factoryType;
private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
public static <F> Provider<F> newFactory(
Class<F> factoryType, Class<?> implementationType){
+ return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType));
+ }
+
+ public static <F> Provider<F> newFactory(
+ TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor
= createMethodMapping(factoryType, implementationType);
@@ -159,7 +166,7 @@
}
}
- private FactoryProvider(Class<F> factoryType,
+ private FactoryProvider(TypeLiteral<F> factoryType,
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
this.factoryType = factoryType;
this.factoryMethodToConstructor = factoryMethodToConstructor;
@@ -208,14 +215,16 @@
return parameter.isBound(injector);
}
- @SuppressWarnings({"unchecked"})
private static Map<Method, AssistedConstructor<?>> createMethodMapping(
- Class<?> factoryType, Class<?> implementationType) {
+ TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) {
List<AssistedConstructor<?>> constructors = Lists.newArrayList();
- for (Constructor<?> c : implementationType.getDeclaredConstructors()) {
- if (c.getAnnotation(AssistedInject.class) != null) {
- constructors.add(new AssistedConstructor(c));
+ for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) {
+ if (constructor.getAnnotation(AssistedInject.class) != null) {
+ @SuppressWarnings("unchecked") // the constructor type and implementation type agree
+ AssistedConstructor assistedConstructor = new AssistedConstructor(
+ constructor, implementationType.getParameterTypes(constructor));
+ constructors.add(assistedConstructor);
}
}
@@ -223,10 +232,12 @@
return ImmutableMap.of();
}
- if (constructors.size() != factoryType.getMethods().length) {
+ Method[] factoryMethods = factoryType.getRawType().getMethods();
+
+ if (constructors.size() != factoryMethods.length) {
throw newConfigurationException("Constructor mismatch: %s has %s @AssistedInject "
- + "constructors, factory %s has %s creation methods", implementationType.getSimpleName(),
- constructors.size(), factoryType.getSimpleName(), factoryType.getMethods().length);
+ + "constructors, factory %s has %s creation methods", implementationType,
+ constructors.size(), factoryType, factoryMethods.length);
}
Map<ParameterListKey, AssistedConstructor> paramsToConstructor = Maps.newHashMap();
@@ -239,13 +250,17 @@
}
Map<Method, AssistedConstructor<?>> result = Maps.newHashMap();
- for (Method method : factoryType.getMethods()) {
- if (!method.getReturnType().isAssignableFrom(implementationType)) {
- throw new RuntimeException(String.format("Return type of method \"%s\""
- + " is not assignable from class \"%s\"", method,
- implementationType.getName()));
+ for (Method method : factoryMethods) {
+ if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
+ throw newConfigurationException("Return type of method %s is not assignable from %s",
+ method, implementationType);
}
- ParameterListKey methodParams = new ParameterListKey(method.getGenericParameterTypes());
+
+ List<Type> parameterTypes = Lists.newArrayList();
+ for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
+ parameterTypes.add(parameterType.getType());
+ }
+ ParameterListKey methodParams = new ParameterListKey(parameterTypes);
if (!paramsToConstructor.containsKey(methodParams)) {
throw newConfigurationException("%s has no @AssistInject constructor that takes the "
@@ -319,8 +334,10 @@
}
};
- return factoryType.cast(Proxy.newProxyInstance(factoryType.getClassLoader(),
- new Class[] {factoryType}, invocationHandler));
+ @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
+ Class<F> factoryRawType = (Class) factoryType.getRawType();
+ return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
+ new Class[] { factoryRawType }, invocationHandler));
}
private static ConfigurationException newConfigurationException(String format, Object... args) {
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
index 06e296b..f1cd29f 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
@@ -41,7 +41,6 @@
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
-import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
@@ -94,25 +93,29 @@
* @param producedType a concrete type that is assignable to the return types of all factory
* methods.
*/
- FactoryProvider2(Class<F> factoryType, Key<?> producedType) {
+ FactoryProvider2(TypeLiteral<F> factoryType, Key<?> producedType) {
this.producedType = producedType;
Errors errors = new Errors();
+
+ @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
+ Class<F> factoryRawType = (Class) factoryType.getRawType();
+
try {
ImmutableMap.Builder<Method, Key<?>> returnTypesBuilder = ImmutableMap.builder();
ImmutableMap.Builder<Method, ImmutableList<Key<?>>> paramTypesBuilder
= ImmutableMap.builder();
// TODO: also grab methods from superinterfaces
- for (Method method : factoryType.getMethods()) {
- Key<?> returnType = getKey(TypeLiteral.get(method.getGenericReturnType()),
- method, method.getAnnotations(), errors);
+ for (Method method : factoryRawType.getMethods()) {
+ Key<?> returnType = getKey(
+ factoryType.getReturnType(method), method, method.getAnnotations(), errors);
returnTypesBuilder.put(method, returnType);
- Type[] params = method.getGenericParameterTypes();
+ List<TypeLiteral<?>> params = factoryType.getParameterTypes(method);
Annotation[][] paramAnnotations = method.getParameterAnnotations();
int p = 0;
List<Key<?>> keys = Lists.newArrayList();
- for (Type param : params) {
- Key<?> paramKey = getKey(TypeLiteral.get(param), method, paramAnnotations[p++], errors);
+ for (TypeLiteral<?> param : params) {
+ Key<?> paramKey = getKey(param, method, paramAnnotations[p++], errors);
keys.add(assistKey(method, paramKey, errors));
}
paramTypesBuilder.put(method, ImmutableList.copyOf(keys));
@@ -123,8 +126,8 @@
throw new ConfigurationException(e.getErrors().getMessages());
}
- factory = factoryType.cast(Proxy.newProxyInstance(factoryType.getClassLoader(),
- new Class[] { factoryType }, this));
+ factory = factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
+ new Class[] { factoryRawType }, this));
}
public F get() {
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java b/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java
index 2b7c8c0..d0c4e74 100755
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java
@@ -93,11 +93,10 @@
}
private boolean isBound(Injector injector, Key<?> key) {
- /* This method is particularly lame - we really need an API that can test
- for any binding, implicit or explicit */
+ // This method is particularly lame - we really need an API that can test
+ // for any binding, implicit or explicit
try {
- return injector.getBinding(key) != null
- || injector.getProvider(key) != null;
+ return injector.getBinding(key) != null;
} catch (ConfigurationException e) {
return false;
}
@@ -123,12 +122,12 @@
}
private Type getProvidedType(Type type) {
- return ((ParameterizedType)type).getActualTypeArguments()[0];
+ return ((ParameterizedType) type).getActualTypeArguments()[0];
}
private boolean isProvider(Type type) {
return type instanceof ParameterizedType
- && ((ParameterizedType)type).getRawType() == Provider.class;
+ && ((ParameterizedType) type).getRawType() == Provider.class;
}
private Key<?> getBindingForType(Type type) {
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
index 79bd1ae..b8aa296 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
@@ -24,6 +24,7 @@
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
+import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.Matchers;
@@ -688,4 +689,194 @@
assertEqualsBothWays(FactoryProvider2.DEFAULT_ANNOTATION, plainAssisted);
assertEquals(FactoryProvider2.DEFAULT_ANNOTATION.toString(), plainAssisted.toString());
}
+
+
+ interface GenericColoredCarFactory<T extends Car> {
+ T create(Color color);
+ }
+
+ public void testGenericAssistedFactory() {
+ final TypeLiteral<GenericColoredCarFactory<Mustang>> mustangTypeLiteral
+ = new TypeLiteral<GenericColoredCarFactory<Mustang>>() {};
+ final TypeLiteral<GenericColoredCarFactory<Camaro>> camaroTypeLiteral
+ = new TypeLiteral<GenericColoredCarFactory<Camaro>>() {};
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Double.class).toInstance(5.0d);
+ bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
+ bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
+ bind(mustangTypeLiteral)
+ .toProvider(FactoryProvider.newFactory(mustangTypeLiteral, TypeLiteral.get(Mustang.class)));
+ bind(camaroTypeLiteral)
+ .toProvider(FactoryProvider.newFactory(camaroTypeLiteral, TypeLiteral.get(Camaro.class)));
+ }
+ });
+
+ GenericColoredCarFactory<Mustang> mustangFactory
+ = injector.getInstance(Key.get(mustangTypeLiteral));
+ GenericColoredCarFactory<Camaro> camaroFactory
+ = injector.getInstance(Key.get(camaroTypeLiteral));
+
+ Mustang blueMustang = mustangFactory.create(Color.BLUE);
+ assertEquals(Color.BLUE, blueMustang.color);
+ assertEquals(5.0d, blueMustang.engineSize);
+
+ Camaro redCamaro = camaroFactory.create(Color.RED);
+ assertEquals(Color.RED, redCamaro.color);
+ assertEquals(1984, redCamaro.modelYear);
+ assertEquals(250, redCamaro.horsePower);
+ }
+
+ public interface Insurance<T extends Car> {
+ }
+
+ public static class MustangInsurance implements Insurance<Mustang> {
+ private final double premium;
+ private final double limit;
+ private Mustang car;
+
+ @Inject
+ public MustangInsurance(@Named("lowLimit") double limit, @Assisted Mustang car,
+ @Assisted double premium) {
+ this.premium = premium;
+ this.limit = limit;
+ this.car = car;
+ }
+
+ public void sell() {}
+ }
+
+ public static class CamaroInsurance implements Insurance<Camaro> {
+ private final double premium;
+ private final double limit;
+ private Camaro car;
+
+ @Inject
+ public CamaroInsurance(@Named("highLimit") double limit, @Assisted Camaro car,
+ @Assisted double premium) {
+ this.premium = premium;
+ this.limit = limit;
+ this.car = car;
+ }
+
+ public void sell() {}
+ }
+
+ public interface MustangInsuranceFactory {
+ public Insurance<Mustang> create(Mustang car, double premium);
+ }
+
+ public interface CamaroInsuranceFactory {
+ public Insurance<Camaro> create(Camaro car, double premium);
+ }
+
+ public void testAssistedFactoryForConcreteType() {
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
+ bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
+ bind(MustangInsuranceFactory.class).toProvider(
+ FactoryProvider.newFactory(MustangInsuranceFactory.class, MustangInsurance.class));
+ bind(CamaroInsuranceFactory.class).toProvider(
+ FactoryProvider.newFactory(CamaroInsuranceFactory.class, CamaroInsurance.class));
+ }
+ });
+
+ MustangInsuranceFactory mustangInsuranceFactory =
+ injector.getInstance(MustangInsuranceFactory.class);
+ CamaroInsuranceFactory camaroInsuranceFactory =
+ injector.getInstance(CamaroInsuranceFactory.class);
+
+ Mustang mustang = new Mustang(5000d, Color.BLACK);
+ MustangInsurance mustangPolicy =
+ (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
+ assertEquals(800.0d, mustangPolicy.premium);
+ assertEquals(50000.0d, mustangPolicy.limit);
+
+ Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+ CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+ assertEquals(800.0d, camaroPolicy.premium);
+ assertEquals(100000.0d, camaroPolicy.limit);
+ }
+
+ public interface InsuranceFactory<T extends Car> {
+ public Insurance<T> create(T car, double premium);
+ }
+
+ public void testAssistedFactoryForParameterizedType() {
+ final TypeLiteral<InsuranceFactory<Mustang>> mustangInsuranceFactoryType =
+ new TypeLiteral<InsuranceFactory<Mustang>>() {};
+ final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
+ new TypeLiteral<InsuranceFactory<Camaro>>() {};
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
+ bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
+ bind(mustangInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+ mustangInsuranceFactoryType, TypeLiteral.get(MustangInsurance.class)));
+ bind(camaroInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+ camaroInsuranceFactoryType, TypeLiteral.get(CamaroInsurance.class)));
+ }
+ });
+
+ InsuranceFactory<Mustang> mustangInsuranceFactory =
+ injector.getInstance(Key.get(mustangInsuranceFactoryType));
+ InsuranceFactory<Camaro> camaroInsuranceFactory =
+ injector.getInstance(Key.get(camaroInsuranceFactoryType));
+
+ Mustang mustang = new Mustang(5000d, Color.BLACK);
+ MustangInsurance mustangPolicy =
+ (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
+ assertEquals(800.0d, mustangPolicy.premium);
+ assertEquals(50000.0d, mustangPolicy.limit);
+
+ Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+ CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+ assertEquals(800.0d, camaroPolicy.premium);
+ assertEquals(100000.0d, camaroPolicy.limit);
+ }
+
+ public static class AutoInsurance<T extends Car> implements Insurance<T> {
+ private final double premium;
+ private final double limit;
+ private final T car;
+
+ @Inject
+ public AutoInsurance(double limit, @Assisted T car, @Assisted double premium) {
+ this.limit = limit;
+ this.car = car;
+ this.premium = premium;
+ }
+
+ public void sell() {}
+ }
+
+ public void testAssistedFactoryForTypeVariableParameters() {
+ final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
+ new TypeLiteral<InsuranceFactory<Camaro>>() {};
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Double.class).toInstance(50000.0d);
+ bind(camaroInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+ camaroInsuranceFactoryType, new TypeLiteral<AutoInsurance<Camaro>>() {}));
+ }
+ });
+
+ InsuranceFactory<Camaro> camaroInsuranceFactory =
+ injector.getInstance(Key.get(camaroInsuranceFactoryType));
+
+ Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+ AutoInsurance camaroPolicy = (AutoInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+ assertEquals(800.0d, camaroPolicy.premium);
+ assertEquals(50000.0d, camaroPolicy.limit);
+ assertEquals(camaro, camaroPolicy.car);
+ }
}
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
index ea96df3..cc77001 100755
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
@@ -577,4 +577,193 @@
interface AssistedParamsFactory {
Car create(@Assisted Color color);
}
+
+ interface GenericColoredCarFactory<T extends Car> {
+ T create(Color color);
+ }
+
+ public void testGenericAssistedFactory() {
+ final TypeLiteral<GenericColoredCarFactory<Mustang>> mustangTypeLiteral
+ = new TypeLiteral<GenericColoredCarFactory<Mustang>>() {};
+ final TypeLiteral<GenericColoredCarFactory<Camaro>> camaroTypeLiteral
+ = new TypeLiteral<GenericColoredCarFactory<Camaro>>() {};
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Double.class).toInstance(5.0d);
+ bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
+ bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
+ bind(mustangTypeLiteral).toProvider(
+ FactoryProvider.newFactory(mustangTypeLiteral, TypeLiteral.get(Mustang.class)));
+ bind(camaroTypeLiteral).toProvider(
+ FactoryProvider.newFactory(camaroTypeLiteral, TypeLiteral.get(Camaro.class)));
+ }
+ });
+
+ GenericColoredCarFactory<Mustang> mustangFactory
+ = injector.getInstance(Key.get(mustangTypeLiteral));
+ GenericColoredCarFactory<Camaro> camaroFactory
+ = injector.getInstance(Key.get(camaroTypeLiteral));
+
+ Mustang blueMustang = mustangFactory.create(Color.BLUE);
+ assertEquals(Color.BLUE, blueMustang.color);
+ assertEquals(5.0d, blueMustang.engineSize);
+
+ Camaro redCamaro = camaroFactory.create(Color.RED);
+ assertEquals(Color.RED, redCamaro.color);
+ assertEquals(1984, redCamaro.modelYear);
+ assertEquals(250, redCamaro.horsePower);
+ }
+
+ public interface Insurance<T extends Car> {
+ }
+
+ public static class MustangInsurance implements Insurance<Mustang> {
+ private final double premium;
+ private final double limit;
+ private Mustang car;
+
+ @AssistedInject
+ public MustangInsurance(@Named("lowLimit") double limit, @Assisted Mustang car,
+ @Assisted double premium) {
+ this.premium = premium;
+ this.limit = limit;
+ this.car = car;
+ }
+
+ public void sell() {}
+ }
+
+ public static class CamaroInsurance implements Insurance<Camaro> {
+ private final double premium;
+ private final double limit;
+ private Camaro car;
+
+ @AssistedInject
+ public CamaroInsurance(@Named("highLimit") double limit, @Assisted Camaro car,
+ @Assisted double premium) {
+ this.premium = premium;
+ this.limit = limit;
+ this.car = car;
+ }
+
+ public void sell() {}
+ }
+
+ public interface MustangInsuranceFactory {
+ public Insurance<Mustang> create(Mustang car, double premium);
+ }
+
+ public interface CamaroInsuranceFactory {
+ public Insurance<Camaro> create(Camaro car, double premium);
+ }
+
+ public void testAssistedFactoryForConcreteType() {
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
+ bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
+ bind(MustangInsuranceFactory.class).toProvider(
+ FactoryProvider.newFactory(MustangInsuranceFactory.class, MustangInsurance.class));
+ bind(CamaroInsuranceFactory.class).toProvider(
+ FactoryProvider.newFactory(CamaroInsuranceFactory.class, CamaroInsurance.class));
+ }
+ });
+
+ MustangInsuranceFactory mustangInsuranceFactory =
+ injector.getInstance(MustangInsuranceFactory.class);
+ CamaroInsuranceFactory camaroInsuranceFactory =
+ injector.getInstance(CamaroInsuranceFactory.class);
+
+ Mustang mustang = new Mustang(5000d, Color.BLACK);
+ MustangInsurance mustangPolicy =
+ (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
+ assertEquals(800.0d, mustangPolicy.premium);
+ assertEquals(50000.0d, mustangPolicy.limit);
+
+ Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+ CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+ assertEquals(800.0d, camaroPolicy.premium);
+ assertEquals(100000.0d, camaroPolicy.limit);
+ }
+
+ public interface InsuranceFactory<T extends Car> {
+ public Insurance<T> create(T car, double premium);
+ }
+
+ public void testAssistedFactoryForParameterizedType() {
+ final TypeLiteral<InsuranceFactory<Mustang>> mustangInsuranceFactoryType =
+ new TypeLiteral<InsuranceFactory<Mustang>>() {};
+ final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
+ new TypeLiteral<InsuranceFactory<Camaro>>() {};
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Double.class).annotatedWith(Names.named("lowLimit")).toInstance(50000.0d);
+ bind(Double.class).annotatedWith(Names.named("highLimit")).toInstance(100000.0d);
+ bind(mustangInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+ mustangInsuranceFactoryType, TypeLiteral.get(MustangInsurance.class)));
+ bind(camaroInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+ camaroInsuranceFactoryType, TypeLiteral.get(CamaroInsurance.class)));
+ }
+ });
+
+ InsuranceFactory<Mustang> mustangInsuranceFactory =
+ injector.getInstance(Key.get(mustangInsuranceFactoryType));
+ InsuranceFactory<Camaro> camaroInsuranceFactory =
+ injector.getInstance(Key.get(camaroInsuranceFactoryType));
+
+ Mustang mustang = new Mustang(5000d, Color.BLACK);
+ MustangInsurance mustangPolicy =
+ (MustangInsurance) mustangInsuranceFactory.create(mustang, 800.0d);
+ assertEquals(800.0d, mustangPolicy.premium);
+ assertEquals(50000.0d, mustangPolicy.limit);
+
+ Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+ CamaroInsurance camaroPolicy = (CamaroInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+ assertEquals(800.0d, camaroPolicy.premium);
+ assertEquals(100000.0d, camaroPolicy.limit);
+ }
+
+ public static class AutoInsurance<T extends Car> implements Insurance<T> {
+ private final double premium;
+ private final double limit;
+ private final T car;
+
+ @AssistedInject
+ public AutoInsurance(double limit, @Assisted T car, @Assisted double premium) {
+ this.limit = limit;
+ this.car = car;
+ this.premium = premium;
+ }
+
+ public void sell() {}
+ }
+
+ public void testAssistedFactoryForTypeVariableParameters() {
+ final TypeLiteral<InsuranceFactory<Camaro>> camaroInsuranceFactoryType =
+ new TypeLiteral<InsuranceFactory<Camaro>>() {};
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Double.class).toInstance(50000.0d);
+ bind(camaroInsuranceFactoryType).toProvider(FactoryProvider.newFactory(
+ camaroInsuranceFactoryType, new TypeLiteral<AutoInsurance<Camaro>>() {}));
+ }
+ });
+
+ InsuranceFactory<Camaro> camaroInsuranceFactory =
+ injector.getInstance(Key.get(camaroInsuranceFactoryType));
+
+ Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
+ AutoInsurance camaroPolicy = (AutoInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+ assertEquals(800.0d, camaroPolicy.premium);
+ assertEquals(50000.0d, camaroPolicy.limit);
+ assertEquals(camaro, camaroPolicy.car);
+ }
}
\ No newline at end of file