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 1929c4ea51..30c3c947d7 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -329,9 +329,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); @@ -345,8 +342,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; @@ -359,6 +357,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) { @@ -1782,6 +1781,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/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index 29db287ee8..dcfc31a897 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -1230,6 +1230,8 @@ class ResolvableTypeTests { ResolvableType consumerUnresolved = ResolvableType.forClass(Consumer.class); ResolvableType consumerObject = ResolvableType.forClassWithGenerics(Consumer.class, Object.class); ResolvableType consumerNestedUnresolved = ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClass(Consumer.class)); + ResolvableType consumerNumber = ResolvableType.forClassWithGenerics(Consumer.class, Number.class); + ResolvableType consumerExtendsNumber = ResolvableType.forClass(SubConsumer.class); assertThat(consumerUnresolved.isAssignableFrom(consumerObject)).isTrue(); assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerObject)).isTrue(); @@ -1239,6 +1241,10 @@ class ResolvableTypeTests { assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerNestedUnresolved)).isTrue(); assertThat(consumerObject.isAssignableFrom(consumerNestedUnresolved)).isFalse(); assertThat(consumerObject.isAssignableFromResolvedPart(consumerNestedUnresolved)).isFalse(); + assertThat(consumerObject.isAssignableFrom(consumerNumber)).isFalse(); + assertThat(consumerObject.isAssignableFromResolvedPart(consumerNumber)).isFalse(); + assertThat(consumerObject.isAssignableFrom(consumerExtendsNumber)).isFalse(); + assertThat(consumerObject.isAssignableFromResolvedPart(consumerExtendsNumber)).isTrue(); } @Test @@ -1788,6 +1794,9 @@ class ResolvableTypeTests { public interface Consumer { } + private static class SubConsumer implements Consumer { + } + public class Wildcard { }