Enforce exact match for bounds of nested type variable

Closes gh-34300
This commit is contained in:
Juergen Hoeller
2025-01-30 15:34:55 +01:00
parent cfe2db0581
commit 4c3b435d23
2 changed files with 28 additions and 5 deletions

View File

@@ -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.
*/

View File

@@ -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<T> {
}
private static class SubConsumer<N extends Number> implements Consumer<N> {
}
public class Wildcard<T extends CharSequence> {
}