Resolve bounds for type variable before emptiness check

Closes gh-34328
This commit is contained in:
Juergen Hoeller
2025-01-30 15:35:14 +01:00
parent 4c3b435d23
commit ed994dcd97
3 changed files with 48 additions and 22 deletions

View File

@@ -180,7 +180,7 @@ public final class GenericTypeResolver {
generics[i] = resolvedTypeArgument; generics[i] = resolvedTypeArgument;
} }
else { else {
generics[i] = ResolvableType.forType(typeArgument).resolveType(); generics[i] = ResolvableType.forType(typeArgument);
} }
} }
else if (typeArgument instanceof ParameterizedType) { else if (typeArgument instanceof ParameterizedType) {
@@ -226,7 +226,7 @@ public final class GenericTypeResolver {
return resolvedType; return resolvedType;
} }
} }
return ResolvableType.NONE; return ResolvableType.forVariableBounds(typeVariable);
} }
/** /**

View File

@@ -953,14 +953,6 @@ public class ResolvableType implements Serializable {
return NONE; return NONE;
} }
@Nullable
private Type resolveBounds(Type[] bounds) {
if (bounds.length == 0 || bounds[0] == Object.class) {
return null;
}
return bounds[0];
}
@Nullable @Nullable
private ResolvableType resolveVariable(TypeVariable<?> variable) { private ResolvableType resolveVariable(TypeVariable<?> variable) {
if (this.type instanceof TypeVariable) { if (this.type instanceof TypeVariable) {
@@ -1463,6 +1455,24 @@ public class ResolvableType implements Serializable {
return new ResolvableType(arrayType, componentType, null, null); return new ResolvableType(arrayType, componentType, null, null);
} }
/**
* Return a {@code ResolvableType} for the bounds of the specified {@link TypeVariable}.
* @param typeVariable the type variable
* @return a {@code ResolvableType} for the specified bounds
* @since 6.2.3
*/
static ResolvableType forVariableBounds(TypeVariable<?> typeVariable) {
return forType(resolveBounds(typeVariable.getBounds()));
}
@Nullable
private static Type resolveBounds(Type[] bounds) {
if (bounds.length == 0 || bounds[0] == Object.class) {
return null;
}
return bounds[0];
}
/** /**
* Return a {@code ResolvableType} for the specified {@link Type}. * Return a {@code ResolvableType} for the specified {@link Type}.
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. * <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
@@ -1491,7 +1501,6 @@ public class ResolvableType implements Serializable {
return forType(type, variableResolver); return forType(type, variableResolver);
} }
/** /**
* Return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}. * Return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}.
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. * <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.

View File

@@ -212,19 +212,27 @@ class GenericTypeResolverTests {
} }
@Test @Test
void resolvedTypeWithBase() { void resolveTypeWithElementBounds() {
Type type = method(WithBaseTypes.class, "get").getGenericReturnType(); Type type = method(WithElementBounds.class, "get").getGenericReturnType();
Type resolvedType = resolveType(type, WithBaseTypes.class); Type resolvedType = resolveType(type, WithElementBounds.class);
ParameterizedTypeReference<List<A>> reference = new ParameterizedTypeReference<>() {}; ParameterizedTypeReference<List<A>> reference = new ParameterizedTypeReference<>() {};
assertThat(resolvedType).isEqualTo(reference.getType()); assertThat(resolvedType).isEqualTo(reference.getType());
} }
@Test
void resolveTypeWithUnresolvableElement() {
Type type = method(WithUnresolvableElement.class, "get").getGenericReturnType();
Type resolvedType = resolveType(type, WithUnresolvableElement.class);
assertThat(resolvedType.toString()).isEqualTo("java.util.List<E>");
}
private static Method method(Class<?> target, String methodName, Class<?>... parameterTypes) { private static Method method(Class<?> target, String methodName, Class<?>... parameterTypes) {
Method method = findMethod(target, methodName, parameterTypes); Method method = findMethod(target, methodName, parameterTypes);
assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull(); assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull();
return method; return method;
} }
public interface MyInterfaceType<T> { public interface MyInterfaceType<T> {
} }
@@ -348,9 +356,9 @@ class GenericTypeResolverTests {
static class GenericClass<T> { static class GenericClass<T> {
} }
class A{} class A {}
class B<T>{} class B<T> {}
class C extends A {} class C extends A {}
@@ -358,23 +366,25 @@ class GenericTypeResolverTests {
class E extends C {} class E extends C {}
class TestIfc<T>{} class TestIfc<T> {}
class TestImpl<I extends A, T extends B<I>> extends TestIfc<T>{ class TestImpl<I extends A, T extends B<I>> extends TestIfc<T> {
} }
abstract static class BiGenericClass<T extends B<?>, V extends A> {} abstract static class BiGenericClass<T extends B<?>, V extends A> {}
abstract static class SpecializedBiGenericClass<U extends C> extends BiGenericClass<D, U>{} abstract static class SpecializedBiGenericClass<U extends C> extends BiGenericClass<D, U> {}
static class TypeFixedBiGenericClass extends SpecializedBiGenericClass<E> {} static class TypeFixedBiGenericClass extends SpecializedBiGenericClass<E> {}
static class TopLevelClass<T> { static class TopLevelClass<T> {
class Nested<X> { class Nested<X> {
} }
} }
static class TypedTopLevelClass extends TopLevelClass<Integer> { static class TypedTopLevelClass extends TopLevelClass<Integer> {
class TypedNested extends Nested<Long> { class TypedNested extends Nested<Long> {
} }
} }
@@ -394,24 +404,31 @@ class GenericTypeResolverTests {
} }
static class WithMethodParameter { static class WithMethodParameter {
public void nestedGenerics(List<Map<String, Integer>> input) { public void nestedGenerics(List<Map<String, Integer>> input) {
} }
} }
public interface ListOfListSupplier<T> { interface ListOfListSupplier<T> {
List<List<T>> get(); List<List<T>> get();
} }
public interface StringListOfListSupplier extends ListOfListSupplier<String> { interface StringListOfListSupplier extends ListOfListSupplier<String> {
} }
static class WithBaseTypes { static class WithElementBounds {
<T extends A> List<T> get() { <T extends A> List<T> get() {
return List.of(); return List.of();
} }
}
static class WithUnresolvableElement<T> {
List<T> get() {
return List.of();
}
} }
} }