Skip to content

Commit 4c3b435

Browse files
committed
Enforce exact match for bounds of nested type variable
Closes gh-34300
1 parent cfe2db0 commit 4c3b435

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

spring-core/src/main/java/org/springframework/core/ResolvableType.java

+19-5
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,6 @@ private boolean isAssignableFrom(ResolvableType other, boolean strict,
329329
other.getComponentType(), true, matchedBefore, upUntilUnresolvable));
330330
}
331331

332-
// We're checking nested generic variables now...
333-
boolean exactMatch = (strict && matchedBefore != null);
334-
335332
// Deal with wildcard bounds
336333
WildcardBounds ourBounds = WildcardBounds.get(this);
337334
WildcardBounds otherBounds = WildcardBounds.get(other);
@@ -345,8 +342,9 @@ private boolean isAssignableFrom(ResolvableType other, boolean strict,
345342
else if (upUntilUnresolvable) {
346343
return otherBounds.isAssignableFrom(this, matchedBefore);
347344
}
348-
else if (!exactMatch) {
349-
return otherBounds.isAssignableTo(this, matchedBefore);
345+
else if (!strict) {
346+
return (matchedBefore != null ? otherBounds.equalsType(this) :
347+
otherBounds.isAssignableTo(this, matchedBefore));
350348
}
351349
else {
352350
return false;
@@ -359,6 +357,7 @@ else if (!exactMatch) {
359357
}
360358

361359
// Main assignability check about to follow
360+
boolean exactMatch = (strict && matchedBefore != null);
362361
boolean checkGenerics = true;
363362
Class<?> ourResolved = null;
364363
if (this.type instanceof TypeVariable<?> variable) {
@@ -1782,6 +1781,21 @@ public boolean isAssignableTo(ResolvableType type, @Nullable Map<Type, Type> mat
17821781
}
17831782
}
17841783

1784+
/**
1785+
* Return {@code true} if these bounds are equal to the specified type.
1786+
* @param type the type to test against
1787+
* @return {@code true} if these bounds are equal to the type
1788+
* @since 6.2.3
1789+
*/
1790+
public boolean equalsType(ResolvableType type) {
1791+
for (ResolvableType bound : this.bounds) {
1792+
if (!type.equalsType(bound)) {
1793+
return false;
1794+
}
1795+
}
1796+
return true;
1797+
}
1798+
17851799
/**
17861800
* Return the underlying bounds.
17871801
*/

spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java

+9
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,8 @@ void strictGenericsMatching() {
12301230
ResolvableType consumerUnresolved = ResolvableType.forClass(Consumer.class);
12311231
ResolvableType consumerObject = ResolvableType.forClassWithGenerics(Consumer.class, Object.class);
12321232
ResolvableType consumerNestedUnresolved = ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClass(Consumer.class));
1233+
ResolvableType consumerNumber = ResolvableType.forClassWithGenerics(Consumer.class, Number.class);
1234+
ResolvableType consumerExtendsNumber = ResolvableType.forClass(SubConsumer.class);
12331235

12341236
assertThat(consumerUnresolved.isAssignableFrom(consumerObject)).isTrue();
12351237
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerObject)).isTrue();
@@ -1239,6 +1241,10 @@ void strictGenericsMatching() {
12391241
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerNestedUnresolved)).isTrue();
12401242
assertThat(consumerObject.isAssignableFrom(consumerNestedUnresolved)).isFalse();
12411243
assertThat(consumerObject.isAssignableFromResolvedPart(consumerNestedUnresolved)).isFalse();
1244+
assertThat(consumerObject.isAssignableFrom(consumerNumber)).isFalse();
1245+
assertThat(consumerObject.isAssignableFromResolvedPart(consumerNumber)).isFalse();
1246+
assertThat(consumerObject.isAssignableFrom(consumerExtendsNumber)).isFalse();
1247+
assertThat(consumerObject.isAssignableFromResolvedPart(consumerExtendsNumber)).isTrue();
12421248
}
12431249

12441250
@Test
@@ -1788,6 +1794,9 @@ public class MyCollectionSuperclassType extends MySuperclassType<Collection<Stri
17881794
public interface Consumer<T> {
17891795
}
17901796

1797+
private static class SubConsumer<N extends Number> implements Consumer<N> {
1798+
}
1799+
17911800
public class Wildcard<T extends CharSequence> {
17921801
}
17931802

0 commit comments

Comments
 (0)