Consistently handle generics in TypeDescriptor.equals
Properly processes recursive types through always comparing generics via the top-level ResolvableType (rather than through nested TypeDescriptors with custom ResolvableType instances). Closes gh-33932
This commit is contained in:
parent
3e3ca74020
commit
7de1dc826a
|
@ -34,7 +34,6 @@ import org.springframework.lang.Contract;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contextual descriptor about a type to convert from or to.
|
* Contextual descriptor about a type to convert from or to.
|
||||||
|
@ -501,16 +500,7 @@ public class TypeDescriptor implements Serializable {
|
||||||
if (!annotationsMatch(otherDesc)) {
|
if (!annotationsMatch(otherDesc)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isCollection() || isArray()) {
|
return Arrays.equals(getResolvableType().getGenerics(), otherDesc.getResolvableType().getGenerics());
|
||||||
return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), otherDesc.getElementTypeDescriptor());
|
|
||||||
}
|
|
||||||
else if (isMap()) {
|
|
||||||
return (ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), otherDesc.getMapKeyTypeDescriptor()) &&
|
|
||||||
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Arrays.equals(getResolvableType().getGenerics(), otherDesc.getResolvableType().getGenerics());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean annotationsMatch(TypeDescriptor otherDesc) {
|
private boolean annotationsMatch(TypeDescriptor otherDesc) {
|
||||||
|
|
|
@ -1387,6 +1387,30 @@ class ResolvableTypeTests {
|
||||||
assertThat(type.hasUnresolvableGenerics()).isFalse();
|
assertThat(type.hasUnresolvableGenerics()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-33932
|
||||||
|
void recursiveType() {
|
||||||
|
assertThat(ResolvableType.forClass(RecursiveMap.class)).isEqualTo(
|
||||||
|
ResolvableType.forClass(RecursiveMap.class));
|
||||||
|
|
||||||
|
ResolvableType resolvableType1 = ResolvableType.forClassWithGenerics(Map.class,
|
||||||
|
String.class, RecursiveMap.class);
|
||||||
|
ResolvableType resolvableType2 = ResolvableType.forClassWithGenerics(Map.class,
|
||||||
|
String.class, RecursiveMap.class);
|
||||||
|
assertThat(resolvableType1).isEqualTo(resolvableType2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-33932
|
||||||
|
void recursiveTypeWithInterface() {
|
||||||
|
assertThat(ResolvableType.forClass(RecursiveMapWithInterface.class)).isEqualTo(
|
||||||
|
ResolvableType.forClass(RecursiveMapWithInterface.class));
|
||||||
|
|
||||||
|
ResolvableType resolvableType1 = ResolvableType.forClassWithGenerics(Map.class,
|
||||||
|
String.class, RecursiveMapWithInterface.class);
|
||||||
|
ResolvableType resolvableType2 = ResolvableType.forClassWithGenerics(Map.class,
|
||||||
|
String.class, RecursiveMapWithInterface.class);
|
||||||
|
assertThat(resolvableType1).isEqualTo(resolvableType2);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void spr11219() throws Exception {
|
void spr11219() throws Exception {
|
||||||
ResolvableType type = ResolvableType.forField(BaseProvider.class.getField("stuff"), BaseProvider.class);
|
ResolvableType type = ResolvableType.forField(BaseProvider.class.getField("stuff"), BaseProvider.class);
|
||||||
|
@ -1836,6 +1860,16 @@ class ResolvableTypeTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
static class RecursiveMap extends HashMap<String, RecursiveMap> {
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
static class RecursiveMapWithInterface extends HashMap<String, RecursiveMapWithInterface>
|
||||||
|
implements Map<String, RecursiveMapWithInterface> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class ResolvableTypeAssert extends AbstractAssert<ResolvableTypeAssert, ResolvableType>{
|
private static class ResolvableTypeAssert extends AbstractAssert<ResolvableTypeAssert, ResolvableType>{
|
||||||
|
|
||||||
public ResolvableTypeAssert(ResolvableType actual) {
|
public ResolvableTypeAssert(ResolvableType actual) {
|
||||||
|
|
|
@ -770,6 +770,30 @@ class TypeDescriptorTests {
|
||||||
assertThat(td1).isNotEqualTo(td2);
|
assertThat(td1).isNotEqualTo(td2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-33932
|
||||||
|
void recursiveType() {
|
||||||
|
assertThat(TypeDescriptor.valueOf(RecursiveMap.class)).isEqualTo(
|
||||||
|
TypeDescriptor.valueOf(RecursiveMap.class));
|
||||||
|
|
||||||
|
TypeDescriptor typeDescriptor1 = TypeDescriptor.map(Map.class,
|
||||||
|
TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(RecursiveMap.class));
|
||||||
|
TypeDescriptor typeDescriptor2 = TypeDescriptor.map(Map.class,
|
||||||
|
TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(RecursiveMap.class));
|
||||||
|
assertThat(typeDescriptor1).isEqualTo(typeDescriptor2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-33932
|
||||||
|
void recursiveTypeWithInterface() {
|
||||||
|
assertThat(TypeDescriptor.valueOf(RecursiveMapWithInterface.class)).isEqualTo(
|
||||||
|
TypeDescriptor.valueOf(RecursiveMapWithInterface.class));
|
||||||
|
|
||||||
|
TypeDescriptor typeDescriptor1 = TypeDescriptor.map(Map.class,
|
||||||
|
TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(RecursiveMapWithInterface.class));
|
||||||
|
TypeDescriptor typeDescriptor2 = TypeDescriptor.map(Map.class,
|
||||||
|
TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(RecursiveMapWithInterface.class));
|
||||||
|
assertThat(typeDescriptor1).isEqualTo(typeDescriptor2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Methods designed for test introspection
|
// Methods designed for test introspection
|
||||||
|
|
||||||
|
@ -987,6 +1011,16 @@ class TypeDescriptorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
static class RecursiveMap extends HashMap<String, RecursiveMap> {
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
static class RecursiveMapWithInterface extends HashMap<String, RecursiveMapWithInterface>
|
||||||
|
implements Map<String, RecursiveMapWithInterface> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Annotations used on tested elements
|
// Annotations used on tested elements
|
||||||
|
|
||||||
@Target({ElementType.PARAMETER})
|
@Target({ElementType.PARAMETER})
|
||||||
|
|
Loading…
Reference in New Issue