From c21cfe0ddfe5b19f2c0b12c47ed5cd7228566aae Mon Sep 17 00:00:00 2001 From: Andriy Matkovsky Date: Wed, 7 Aug 2019 13:05:42 +0300 Subject: [PATCH 1/2] Fix looking up generics when injecting fields in generic subclasses. --- ...roviderMultipleNestedGenericIntoField.java | 40 ++++++++++++++ .../java/com/example/IntegrationTest.java | 14 +++++ .../reflect/ReflectiveMembersInjector.java | 53 ++++++++++++++++++- 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java 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..8dcb9c9d --- /dev/null +++ b/integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java @@ -0,0 +1,40 @@ +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); From 46080cbe46cd4d86fe5fa7a65b9212fb6f82e661 Mon Sep 17 00:00:00 2001 From: Andriy Matkovsky Date: Wed, 7 Aug 2019 13:17:09 +0300 Subject: [PATCH 2/2] Fix formatting. --- .../java/com/example/ProviderMultipleNestedGenericIntoField.java | 1 - 1 file changed, 1 deletion(-) diff --git a/integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java b/integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java index 8dcb9c9d..d185f03a 100644 --- a/integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java +++ b/integration-tests/src/main/java/com/example/ProviderMultipleNestedGenericIntoField.java @@ -1,7 +1,6 @@ package com.example; import dagger.Component; - import javax.inject.Inject; import javax.inject.Provider;