From ed994dcd977b5a48db00c19a7a296b649d8f27c0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 30 Jan 2025 15:35:14 +0100 Subject: [PATCH] Resolve bounds for type variable before emptiness check Closes gh-34328 --- .../core/GenericTypeResolver.java | 4 +- .../springframework/core/ResolvableType.java | 27 ++++++++----- .../core/GenericTypeResolverTests.java | 39 +++++++++++++------ 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java index c27a23cd07e..9e4a9e381a8 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -180,7 +180,7 @@ public final class GenericTypeResolver { generics[i] = resolvedTypeArgument; } else { - generics[i] = ResolvableType.forType(typeArgument).resolveType(); + generics[i] = ResolvableType.forType(typeArgument); } } else if (typeArgument instanceof ParameterizedType) { @@ -226,7 +226,7 @@ public final class GenericTypeResolver { return resolvedType; } } - return ResolvableType.NONE; + return ResolvableType.forVariableBounds(typeVariable); } /** diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 30c3c947d72..594beef070a 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -953,14 +953,6 @@ public class ResolvableType implements Serializable { return NONE; } - @Nullable - private Type resolveBounds(Type[] bounds) { - if (bounds.length == 0 || bounds[0] == Object.class) { - return null; - } - return bounds[0]; - } - @Nullable private ResolvableType resolveVariable(TypeVariable variable) { if (this.type instanceof TypeVariable) { @@ -1463,6 +1455,24 @@ public class ResolvableType implements Serializable { 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}. *

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 a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}. *

Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. diff --git a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java index 1f80cc6c343..fcfff993433 100644 --- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java @@ -212,19 +212,27 @@ class GenericTypeResolverTests { } @Test - void resolvedTypeWithBase() { - Type type = method(WithBaseTypes.class, "get").getGenericReturnType(); - Type resolvedType = resolveType(type, WithBaseTypes.class); + void resolveTypeWithElementBounds() { + Type type = method(WithElementBounds.class, "get").getGenericReturnType(); + Type resolvedType = resolveType(type, WithElementBounds.class); ParameterizedTypeReference> reference = new ParameterizedTypeReference<>() {}; 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"); + } + private static Method method(Class target, String methodName, Class... parameterTypes) { Method method = findMethod(target, methodName, parameterTypes); assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull(); return method; } + public interface MyInterfaceType { } @@ -348,9 +356,9 @@ class GenericTypeResolverTests { static class GenericClass { } - class A{} + class A {} - class B{} + class B {} class C extends A {} @@ -358,23 +366,25 @@ class GenericTypeResolverTests { class E extends C {} - class TestIfc{} + class TestIfc {} - class TestImpl> extends TestIfc{ + class TestImpl> extends TestIfc { } abstract static class BiGenericClass, V extends A> {} - abstract static class SpecializedBiGenericClass extends BiGenericClass{} + abstract static class SpecializedBiGenericClass extends BiGenericClass {} static class TypeFixedBiGenericClass extends SpecializedBiGenericClass {} static class TopLevelClass { + class Nested { } } static class TypedTopLevelClass extends TopLevelClass { + class TypedNested extends Nested { } } @@ -394,24 +404,31 @@ class GenericTypeResolverTests { } static class WithMethodParameter { + public void nestedGenerics(List> input) { } } - public interface ListOfListSupplier { + interface ListOfListSupplier { List> get(); } - public interface StringListOfListSupplier extends ListOfListSupplier { + interface StringListOfListSupplier extends ListOfListSupplier { } - static class WithBaseTypes { + static class WithElementBounds { List get() { return List.of(); } + } + + static class WithUnresolvableElement { + List get() { + return List.of(); + } } }