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 aa72d9bc07..b9d46a3734 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -177,7 +177,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) { @@ -223,7 +223,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 813562b9b4..fc8e60edd3 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -320,9 +320,6 @@ public class ResolvableType implements Serializable { other.getComponentType(), true, matchedBefore, upUntilUnresolvable)); } - // We're checking nested generic variables now... - boolean exactMatch = (strict && matchedBefore != null); - // Deal with wildcard bounds WildcardBounds ourBounds = WildcardBounds.get(this); WildcardBounds otherBounds = WildcardBounds.get(other); @@ -336,8 +333,9 @@ public class ResolvableType implements Serializable { else if (upUntilUnresolvable) { return otherBounds.isAssignableFrom(this, matchedBefore); } - else if (!exactMatch) { - return otherBounds.isAssignableTo(this, matchedBefore); + else if (!strict) { + return (matchedBefore != null ? otherBounds.equalsType(this) : + otherBounds.isAssignableTo(this, matchedBefore)); } else { return false; @@ -350,6 +348,7 @@ public class ResolvableType implements Serializable { } // Main assignability check about to follow + boolean exactMatch = (strict && matchedBefore != null); boolean checkGenerics = true; Class> ourResolved = null; if (this.type instanceof TypeVariable> variable) { @@ -942,13 +941,6 @@ public class ResolvableType implements Serializable { return NONE; } - private @Nullable Type resolveBounds(Type[] bounds) { - if (bounds.length == 0 || bounds[0] == Object.class) { - return null; - } - return bounds[0]; - } - private @Nullable ResolvableType resolveVariable(TypeVariable> variable) { if (this.type instanceof TypeVariable) { return resolveType().resolveVariable(variable); @@ -1449,6 +1441,23 @@ 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())); + } + + private static @Nullable 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}. @@ -1477,7 +1486,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}.
@@ -1763,6 +1771,21 @@ public class ResolvableType implements Serializable {
}
}
+ /**
+ * Return {@code true} if these bounds are equal to the specified type.
+ * @param type the type to test against
+ * @return {@code true} if these bounds are equal to the type
+ * @since 6.2.3
+ */
+ public boolean equalsType(ResolvableType type) {
+ for (ResolvableType bound : this.bounds) {
+ if (!type.equalsType(bound)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Return the underlying bounds.
*/
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 1f80cc6c34..fcfff99343 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