From d497e932112408e8d5c29f37728e3410d0bbc169 Mon Sep 17 00:00:00 2001 From: Alex Dochioiu Date: Fri, 26 Mar 2021 12:19:01 +0000 Subject: [PATCH] Fix injection when using generics --- .../MemberInjectionGenericInjector.java | 23 +++++++++++++++++++ .../java/com/example/IntegrationTest.java | 10 ++++++++ .../reflect/ComponentInvocationHandler.java | 22 +++++++++++++++--- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 integration-tests/src/main/java/com/example/MemberInjectionGenericInjector.java diff --git a/integration-tests/src/main/java/com/example/MemberInjectionGenericInjector.java b/integration-tests/src/main/java/com/example/MemberInjectionGenericInjector.java new file mode 100644 index 00000000..8d43e1e2 --- /dev/null +++ b/integration-tests/src/main/java/com/example/MemberInjectionGenericInjector.java @@ -0,0 +1,23 @@ +package com.example; + +import dagger.BindsInstance; +import dagger.Component; +import javax.inject.Inject; + +@Component +public interface MemberInjectionGenericInjector + extends Injector { + + @Component.Factory + interface Factory { + MemberInjectionGenericInjector create(@BindsInstance String one); + } + + class Target { + @Inject String one; + } +} + +interface Injector { + void inject(T target); +} diff --git a/integration-tests/src/test/java/com/example/IntegrationTest.java b/integration-tests/src/test/java/com/example/IntegrationTest.java index acd586a5..daaef5cc 100644 --- a/integration-tests/src/test/java/com/example/IntegrationTest.java +++ b/integration-tests/src/test/java/com/example/IntegrationTest.java @@ -636,6 +636,16 @@ public void memberInjectionEmptyClass() { // No state, nothing to verify, except it didn't throw. } + @Test + public void memberInjectionGenericInjector() { + final String expectedStr = "expected"; + MemberInjectionGenericInjector component = + backend.factory(MemberInjectionGenericInjector.Factory.class).create(expectedStr); + MemberInjectionGenericInjector.Target target = new MemberInjectionGenericInjector.Target(); + component.inject(target); + assertThat(target.one).isEqualTo(expectedStr); + } + @Test public void memberInjectionEmptyAbstractClass() { MemberInjectionEmptyAbstract component = backend.create(MemberInjectionEmptyAbstract.class); diff --git a/reflect/src/main/java/dagger/reflect/ComponentInvocationHandler.java b/reflect/src/main/java/dagger/reflect/ComponentInvocationHandler.java index 465e4884..691ffaf7 100644 --- a/reflect/src/main/java/dagger/reflect/ComponentInvocationHandler.java +++ b/reflect/src/main/java/dagger/reflect/ComponentInvocationHandler.java @@ -24,6 +24,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.Nullable; @@ -60,8 +61,24 @@ private ComponentInvocationHandler(Scope scope) { } MethodInvocationHandler handler = handlers.get(method); + final Class[] parameterTypes = method.getParameterTypes(); if (handler == null) { - handler = createMethodInvocationHandler(method, scope); + final int argsCount = args != null ? args.length : 0; + for (int index = 0; index < argsCount; ++index) { + final Object arg = args[index]; + if (arg == null) continue; // ignore null arguments + if (!TypeVariable.class.isAssignableFrom( + method.getGenericParameterTypes()[index].getClass())) + continue; // ignore args not using generics + + final Class parameter = parameterTypes[index]; + // Replace generic boundary class with actual class being invoked + if (parameter.isAssignableFrom(arg.getClass())) { + parameterTypes[index] = arg.getClass(); + } + } + + handler = createMethodInvocationHandler(method, parameterTypes, scope); MethodInvocationHandler replaced = handlers.putIfAbsent(method, handler); if (replaced != null) { handler = replaced; @@ -71,9 +88,8 @@ private ComponentInvocationHandler(Scope scope) { } private static ComponentInvocationHandler.MethodInvocationHandler createMethodInvocationHandler( - Method method, Scope scope) { + Method method, Class[] parameterTypes, Scope scope) { Type returnType = method.getGenericReturnType(); - Class[] parameterTypes = method.getParameterTypes(); if (returnType instanceof Class) { Class returnClass = (Class) returnType;