diff --git a/integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java b/integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java new file mode 100644 index 00000000..d185f03a --- /dev/null +++ b/integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java @@ -0,0 +1,39 @@ +package com.example; + +import dagger.Component; +import javax.inject.Inject; +import javax.inject.Provider; + +@Component +interface ProviderMultipleNestedGenericIntoField { + void inject(ThingImpl instance); + + class ThingImpl extends Thing1 { + + @Inject Provider dep2; + } + + abstract class Thing1 extends Thing {} + + abstract class Thing { + @Inject Provider vProvider; + + @Inject Provider tProvider; + + @Inject V value; + + @Inject T t; + } + + class Dep1 { + + @Inject + Dep1() {} + } + + class Dep2 { + + @Inject + Dep2() {} + } +} diff --git a/integration-tests/src/test/java/com/example/IntegrationTest.java b/integration-tests/src/test/java/com/example/IntegrationTest.java index a2c69599..1c559047 100644 --- a/integration-tests/src/test/java/com/example/IntegrationTest.java +++ b/integration-tests/src/test/java/com/example/IntegrationTest.java @@ -328,6 +328,20 @@ public void providerMultipleGenericIntoJustInTimeGeneric() { assertThat(component.thing().valueProvider.get()).isNotNull(); } + @Test + public void providerMultipleNestedGenericIntoField() { + ProviderMultipleNestedGenericIntoField c = + backend.create(ProviderMultipleNestedGenericIntoField.class); + ProviderMultipleNestedGenericIntoField.ThingImpl instance = + new ProviderMultipleNestedGenericIntoField.ThingImpl(); + c.inject(instance); + assertThat(instance.vProvider.get()).isNotNull(); + assertThat(instance.tProvider.get()).isNotNull(); + assertThat(instance.value).isNotNull(); + assertThat(instance.t).isNotNull(); + assertThat(instance.dep2.get()).isNotNull(); + } + @Test public void providerUnscopedBinding() { ProviderUnscopedBinding component = backend.create(ProviderUnscopedBinding.class); diff --git a/reflect/src/main/java/dagger/reflect/ReflectiveMembersInjector.java b/reflect/src/main/java/dagger/reflect/ReflectiveMembersInjector.java index 07586a14..c99b8a47 100644 --- a/reflect/src/main/java/dagger/reflect/ReflectiveMembersInjector.java +++ b/reflect/src/main/java/dagger/reflect/ReflectiveMembersInjector.java @@ -25,9 +25,12 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.util.ArrayDeque; import java.util.Deque; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.inject.Inject; @@ -36,7 +39,26 @@ final class ReflectiveMembersInjector implements MembersInjector { static MembersInjector create(Class cls, Scope scope) { Deque> classInjectors = new ArrayDeque<>(); Class target = cls; + Map> typeVariablesBindings = new HashMap<>(); while (target != Object.class && target != null) { + Type genericSuperclass = target.getGenericSuperclass(); + if (genericSuperclass instanceof ParameterizedType) { + ParameterizedType type = (ParameterizedType) genericSuperclass; + Type[] actualTypeArguments = type.getActualTypeArguments(); + TypeVariable>[] typeParameters = + ((Class) type.getRawType()).getTypeParameters(); + for (int i = 0; i < actualTypeArguments.length; i++) { + Type actualTypeArgument = actualTypeArguments[i]; + if (!(actualTypeArgument instanceof Class)) { + // skip generics + continue; + } + String name = typeParameters[i].getName(); + Class arg = (Class) actualTypeArguments[i]; + typeVariablesBindings.put(name, arg); + } + } + Map> fieldBindings = new LinkedHashMap<>(); for (Field field : target.getDeclaredFields()) { if (field.getAnnotation(Inject.class) == null) { @@ -57,7 +79,36 @@ static MembersInjector create(Class cls, Scope scope) { + field.getName()); } - Key key = Key.of(findQualifier(field.getDeclaredAnnotations()), field.getGenericType()); + Type resultType = null; + Type genericType = field.getGenericType(); + if (genericType instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) genericType; + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + Type[] typeArguments = new Type[parameterizedType.getActualTypeArguments().length]; + for (int i = 0; i < typeArguments.length; i++) { + Type actualTypeArgument = actualTypeArguments[i]; + if (actualTypeArgument instanceof TypeVariable) { + TypeVariable typeVariable = (TypeVariable) actualTypeArgument; + String typeName = typeVariable.getName(); + typeArguments[i] = typeVariablesBindings.get(typeName); + } else { + typeArguments[i] = actualTypeArgument; + } + } + resultType = + new TypeUtil.ParameterizedTypeImpl( + parameterizedType.getOwnerType(), parameterizedType.getRawType(), typeArguments); + } else if (genericType instanceof TypeVariable) { + TypeVariable typeVariable = (TypeVariable) genericType; + resultType = typeVariablesBindings.get(typeVariable.getName()); + if (resultType == null) { + resultType = field.getGenericType(); + } + } else { + resultType = field.getGenericType(); + } + + Key key = Key.of(findQualifier(field.getDeclaredAnnotations()), resultType); LinkedBinding binding = scope.getBinding(key); fieldBindings.put(field, binding);