From 5d3c9b1c8c661a575ad33306a3337325f53fd2e0 Mon Sep 17 00:00:00 2001 From: Laimonas Turauskas Date: Fri, 9 Aug 2019 10:51:28 -0700 Subject: [PATCH] Add support for generic builder interface. --- .../GenericComponentBuilderInterface.java | 18 +++++++++++++ .../java/com/example/IntegrationTest.java | 8 ++++++ .../ComponentBuilderInvocationHandler.java | 26 ++++++++++++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 integration-tests/src/main/java/com/example/GenericComponentBuilderInterface.java diff --git a/integration-tests/src/main/java/com/example/GenericComponentBuilderInterface.java b/integration-tests/src/main/java/com/example/GenericComponentBuilderInterface.java new file mode 100644 index 00000000..7ee84c5d --- /dev/null +++ b/integration-tests/src/main/java/com/example/GenericComponentBuilderInterface.java @@ -0,0 +1,18 @@ +package com.example; + +import dagger.BindsInstance; +import dagger.Component; + +@Component +public interface GenericComponentBuilderInterface { + String value(); + + @Component.Builder + interface Builder extends GenericBuilder { + Builder bindString(@BindsInstance String instance); + } + + public interface GenericBuilder { + Component build(); + } +} diff --git a/integration-tests/src/test/java/com/example/IntegrationTest.java b/integration-tests/src/test/java/com/example/IntegrationTest.java index a2c69599..9dace575 100644 --- a/integration-tests/src/test/java/com/example/IntegrationTest.java +++ b/integration-tests/src/test/java/com/example/IntegrationTest.java @@ -1450,6 +1450,14 @@ public void subcomponentFactoryProvision() { assertThat(nested.two()).isEqualTo(2L); } + @Test + public void genericComponentBuilderInterface() { + GenericComponentBuilderInterface component = + backend.builder(GenericComponentBuilderInterface.Builder.class).bindString("one").build(); + + assertThat(component.value()).isEqualTo("one"); + } + @Test public void componentBindingInstance() { ComponentBindingInstance instance = backend.create(ComponentBindingInstance.class); diff --git a/reflect/src/main/java/dagger/reflect/ComponentBuilderInvocationHandler.java b/reflect/src/main/java/dagger/reflect/ComponentBuilderInvocationHandler.java index 916d0f44..de2e3a5b 100644 --- a/reflect/src/main/java/dagger/reflect/ComponentBuilderInvocationHandler.java +++ b/reflect/src/main/java/dagger/reflect/ComponentBuilderInvocationHandler.java @@ -28,7 +28,9 @@ import java.lang.reflect.InvocationHandler; 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; final class ComponentBuilderInvocationHandler implements InvocationHandler { static B forComponentBuilder(Class builderClass) { @@ -87,7 +89,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return method.invoke(proxy, args); } - Class returnType = method.getReturnType(); + Type returnType = resolveReturnType(method); Type[] parameterTypes = method.getGenericParameterTypes(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); @@ -166,4 +168,26 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl throw new IllegalStateException(method.toString()); // TODO report unsupported method shape } + + private Type resolveReturnType(Method method) { + Type genericReturnType = method.getGenericReturnType(); + + if (genericReturnType != null) { + Class declaringClass = method.getDeclaringClass(); + TypeVariable>[] typeParameters = declaringClass.getTypeParameters(); + for (int i = 0; i < typeParameters.length; i++) { + if (typeParameters[i].equals(genericReturnType)) { + Type[] genericInterfaces = builderClass.getGenericInterfaces(); + for (Type genericInterface : genericInterfaces) { + ParameterizedType implementationType = (ParameterizedType) genericInterface; + if (implementationType.getRawType().equals(declaringClass)) { + return implementationType.getActualTypeArguments()[i]; + } + } + } + } + } + + return method.getReturnType(); + } }