Restrict lenient nested matching to immediate type variable
Includes fix for matching multiple wildcard bounds properly. Closes gh-34119 Closes gh-34234
This commit is contained in:
parent
d280358e98
commit
227385083d
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -410,8 +410,9 @@ public class ResolvableType implements Serializable {
|
||||||
}
|
}
|
||||||
matchedBefore.put(this.type, other.type);
|
matchedBefore.put(this.type, other.type);
|
||||||
for (int i = 0; i < ourGenerics.length; i++) {
|
for (int i = 0; i < ourGenerics.length; i++) {
|
||||||
if (!ourGenerics[i].isAssignableFrom(otherGenerics[i],
|
ResolvableType otherGeneric = otherGenerics[i];
|
||||||
!other.hasUnresolvableGenerics(), matchedBefore, upUntilUnresolvable)) {
|
if (!ourGenerics[i].isAssignableFrom(otherGeneric,
|
||||||
|
!otherGeneric.isUnresolvableTypeVariable(), matchedBefore, upUntilUnresolvable)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1729,8 +1730,16 @@ public class ResolvableType implements Serializable {
|
||||||
* @return {@code true} if these bounds are assignable from all types
|
* @return {@code true} if these bounds are assignable from all types
|
||||||
*/
|
*/
|
||||||
public boolean isAssignableFrom(ResolvableType[] types, @Nullable Map<Type, Type> matchedBefore) {
|
public boolean isAssignableFrom(ResolvableType[] types, @Nullable Map<Type, Type> matchedBefore) {
|
||||||
for (ResolvableType type : types) {
|
for (ResolvableType bound : this.bounds) {
|
||||||
if (!isAssignableFrom(type, matchedBefore)) {
|
boolean matched = false;
|
||||||
|
for (ResolvableType type : types) {
|
||||||
|
if (this.kind == Kind.UPPER ? bound.isAssignableFrom(type, false, matchedBefore, false) :
|
||||||
|
type.isAssignableFrom(bound, false, matchedBefore, false)) {
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matched) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -1189,10 +1189,11 @@ class ResolvableTypeTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void isAssignableFromForUnresolvedWildcards() {
|
void isAssignableFromForUnresolvedWildcard() {
|
||||||
ResolvableType wildcard = ResolvableType.forInstance(new Wildcard<>());
|
ResolvableType wildcard = ResolvableType.forInstance(new Wildcard<>());
|
||||||
ResolvableType wildcardFixed = ResolvableType.forInstance(new WildcardFixed());
|
ResolvableType wildcardFixed = ResolvableType.forInstance(new WildcardFixed());
|
||||||
ResolvableType wildcardConcrete = ResolvableType.forClassWithGenerics(Wildcard.class, Number.class);
|
ResolvableType wildcardConcrete = ResolvableType.forClassWithGenerics(Wildcard.class, CharSequence.class);
|
||||||
|
ResolvableType wildcardConsumer = ResolvableType.forInstance(new WildcardConsumer<>());
|
||||||
|
|
||||||
assertThat(wildcard.isAssignableFrom(wildcardFixed)).isTrue();
|
assertThat(wildcard.isAssignableFrom(wildcardFixed)).isTrue();
|
||||||
assertThat(wildcard.isAssignableFromResolvedPart(wildcardFixed)).isTrue();
|
assertThat(wildcard.isAssignableFromResolvedPart(wildcardFixed)).isTrue();
|
||||||
|
@ -1206,6 +1207,38 @@ class ResolvableTypeTests {
|
||||||
assertThat(wildcardConcrete.isAssignableFromResolvedPart(wildcard)).isTrue();
|
assertThat(wildcardConcrete.isAssignableFromResolvedPart(wildcard)).isTrue();
|
||||||
assertThat(wildcardConcrete.isAssignableFrom(wildcardFixed)).isFalse();
|
assertThat(wildcardConcrete.isAssignableFrom(wildcardFixed)).isFalse();
|
||||||
assertThat(wildcardConcrete.isAssignableFromResolvedPart(wildcardFixed)).isFalse();
|
assertThat(wildcardConcrete.isAssignableFromResolvedPart(wildcardFixed)).isFalse();
|
||||||
|
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFrom(wildcard)).isFalse();
|
||||||
|
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFromResolvedPart(wildcard)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAssignableFromForUnresolvedDoubleWildcard() {
|
||||||
|
ResolvableType wildcard = ResolvableType.forInstance(new DoubleWildcard<>());
|
||||||
|
ResolvableType wildcardFixed = ResolvableType.forInstance(new DoubleWildcardFixed());
|
||||||
|
ResolvableType wildcardConsumer = ResolvableType.forInstance(new DoubleWildcardConsumer<>());
|
||||||
|
|
||||||
|
assertThat(wildcard.isAssignableFrom(wildcardFixed)).isTrue();
|
||||||
|
assertThat(wildcard.isAssignableFromResolvedPart(wildcardFixed)).isTrue();
|
||||||
|
assertThat(wildcardFixed.isAssignableFrom(wildcard)).isFalse();
|
||||||
|
assertThat(wildcardFixed.isAssignableFromResolvedPart(wildcard)).isFalse();
|
||||||
|
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFrom(wildcard)).isTrue();
|
||||||
|
assertThat(wildcardConsumer.as(Consumer.class).getGeneric().isAssignableFromResolvedPart(wildcard)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void strictGenericsMatching() {
|
||||||
|
ResolvableType consumerUnresolved = ResolvableType.forClass(Consumer.class);
|
||||||
|
ResolvableType consumerObject = ResolvableType.forClassWithGenerics(Consumer.class, Object.class);
|
||||||
|
ResolvableType consumerNestedUnresolved = ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClass(Consumer.class));
|
||||||
|
|
||||||
|
assertThat(consumerUnresolved.isAssignableFrom(consumerObject)).isTrue();
|
||||||
|
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerObject)).isTrue();
|
||||||
|
assertThat(consumerObject.isAssignableFrom(consumerUnresolved)).isTrue();
|
||||||
|
assertThat(consumerObject.isAssignableFromResolvedPart(consumerUnresolved)).isTrue();
|
||||||
|
assertThat(consumerUnresolved.isAssignableFrom(consumerNestedUnresolved)).isTrue();
|
||||||
|
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerNestedUnresolved)).isTrue();
|
||||||
|
assertThat(consumerObject.isAssignableFrom(consumerNestedUnresolved)).isFalse();
|
||||||
|
assertThat(consumerObject.isAssignableFromResolvedPart(consumerNestedUnresolved)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1752,12 +1785,26 @@ class ResolvableTypeTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class Wildcard<T extends Number> {
|
public class Wildcard<T extends CharSequence> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WildcardFixed extends Wildcard<Integer> {
|
public class WildcardFixed extends Wildcard<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class WildcardConsumer<T extends CharSequence & Serializable> implements Consumer<Wildcard<T>> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class DoubleWildcard<T extends CharSequence & Serializable> {
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DoubleWildcardFixed extends DoubleWildcard<String> {
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DoubleWildcardConsumer<T extends CharSequence & Serializable> implements Consumer<DoubleWildcard<T>> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface VariableNameSwitch<V, K> extends MultiValueMap<K, V> {
|
interface VariableNameSwitch<V, K> extends MultiValueMap<K, V> {
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue