Skip to content
This repository was archived by the owner on Aug 20, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example;

import dagger.Component;
import dagger.Module;
import dagger.Provides;
import javax.inject.Inject;
import javax.inject.Provider;

@Component(modules = JustInTimeGenericNested.Module1.class)
interface JustInTimeGenericNested {
Registry<String> thing();

@Module
abstract class Module1 {
@Provides
static String provideString() {
return "foo";
}
}

final class Registry<T> {

@Inject
Registry(JobProvider<T> jobProvider) {}
}

final class JobProvider<V> {

@Inject
public JobProvider(Provider<Job<V>> jobProvider) {}
}

final class Job<R> {

@Inject
public Job(Provider<R> provider) {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ public void justInTimeGeneric() {
assertThat(component.thing()).isNotNull();
}

@Test
public void justInTimeGenericNested() {
JustInTimeGenericNested component = backend.create(JustInTimeGenericNested.class);
assertThat(component.thing()).isNotNull();
}

@Test
public void justInTimeMembersInjection() {
JustInTimeMembersInjection component = backend.create(JustInTimeMembersInjection.class);
Expand Down
20 changes: 20 additions & 0 deletions reflect/src/main/java/dagger/reflect/Key.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,39 @@

import com.google.auto.value.AutoValue;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import org.jetbrains.annotations.Nullable;

@AutoValue
abstract class Key {
static Key of(@Nullable Annotation qualifier, Type type) {
if (containsTypeVariable(type)) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JakeWharton I can remove this check if it is causing any second thoughts on merging this PR.
It helps surface unsupported bindings in Dagger Reflect as soon as possible. When a Key which includes <T> instead of a real type, it immediately throws an error.
It has a bit of a runtime cost since it recursively checks the type and its parameters whenever a Key is constructed.

throw new IllegalArgumentException("cannot contain type variable " + type);
}
return new AutoValue_Key(qualifier, canonicalize(boxIfNecessary(type)));
}

abstract @Nullable Annotation qualifier();

abstract Type type();

private static boolean containsTypeVariable(Type type) {
if (type instanceof TypeVariable) {
return true;
}
if (type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
for (Type arg : actualTypeArguments) {
if (containsTypeVariable(arg)) {
return true;
}
}
}
return false;
}

@Override
public final String toString() {
Annotation qualifier = qualifier();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,24 @@ private static boolean hasParameterizedTypeVariable(Type parameterType) {
if (isTypeVariable(type)) {
return true;
}
if (hasParameterizedTypeVariable(type)) {
return true;
}
}
return false;
}

private TypeUtil.ParameterizedTypeImpl findKeyForParameterizedType(
ParameterizedType parameterType) {
Type[] matchingTypes = matchingParameterizedType(parameterType.getActualTypeArguments());
return new TypeUtil.ParameterizedTypeImpl(null, parameterType.getRawType(), matchingTypes);
return new TypeUtil.ParameterizedTypeImpl(
parameterType.getOwnerType(), parameterType.getRawType(), matchingTypes);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JakeWharton i wasn't passing in the owner type here which was causing the exception. it is now resolved.

}

/**
* Find matching concrete types for a list of types. For every TypeVariable like `T` in the array
* arg, we lookup the matching type in this class's concrete type arguments. If it is already a
* concrete type, just return the type. Creates a new array to match parameterizd type.
* concrete type, just return the type. Creates a new array to match parameterized type.
*
* @param typeArguments The Types and TypeVariables to find matching concrete types for.
* @return The matching concrete type for the placeholder.
Expand All @@ -88,6 +92,8 @@ private Type[] matchingParameterizedType(Type[] typeArguments) {
for (int i = 0; i < typeArguments.length; i++) {
if (isTypeVariable(typeArguments[i])) {
matchedTypeArguments[i] = matchTypeToConcreteType((TypeVariable<?>) typeArguments[i]);
} else if (typeArguments[i] instanceof ParameterizedType) {
matchedTypeArguments[i] = findKeyForParameterizedType((ParameterizedType) typeArguments[i]);
} else {
matchedTypeArguments[i] = typeArguments[i];
}
Expand Down