Consider nested generics in TypeDiscoverer equality comparison.

We now compare nested generics wrapped into TypeInformation to consider type equality for deeply parametrized types.

Previously, we resolved type parameters to Class so Foo<List<String>> was considered equal to Foo<List<Map>> as the type parameter of the first nesting level was erased.

Closes #3051
This commit is contained in:
Mark Paluch
2024-02-27 11:18:29 +01:00
parent 1380988743
commit 43caf4970c
2 changed files with 28 additions and 9 deletions

View File

@@ -60,6 +60,8 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
private final Map<Constructor<?>, List<TypeInformation<?>>> constructorParameters = new ConcurrentHashMap<>();
private final Lazy<List<TypeInformation<?>>> typeArguments;
private final Lazy<List<TypeInformation<?>>> resolvedGenerics;
protected TypeDiscoverer(ResolvableType type) {
Assert.notNull(type, "Type must not be null");
@@ -68,6 +70,10 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
this.componentType = Lazy.of(this::doGetComponentType);
this.valueType = Lazy.of(this::doGetMapValueType);
this.typeArguments = Lazy.of(this::doGetTypeArguments);
this.resolvedGenerics = Lazy.of(() -> Arrays.stream(resolvableType.getGenerics()) //
.map(TypeInformation::of) // use TypeInformation comparison to remove any attachments to variableResolver
// holding the type source
.collect(Collectors.toList()));
}
static TypeDiscoverer<?> td(ResolvableType type) {
@@ -325,15 +331,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
return false;
}
var collect1 = Arrays.stream(resolvableType.getGenerics()) //
.map(ResolvableType::toClass) //
.collect(Collectors.toList());
var collect2 = Arrays.stream(that.resolvableType.getGenerics()) //
.map(ResolvableType::toClass) //
.collect(Collectors.toList());
return ObjectUtils.nullSafeEquals(collect1, collect2);
return ObjectUtils.nullSafeEquals(resolvedGenerics.get(), that.resolvedGenerics.get());
}
@Override

View File

@@ -29,6 +29,7 @@ import java.util.Set;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.core.ResolvableType;
import org.springframework.data.geo.GeoResults;
@@ -348,6 +349,15 @@ public class TypeDiscovererUnitTests {
assertThat(discoverer.hashCode()).isNotEqualTo(classTypeInformation.hashCode());
}
@Test // GH-3051
void considersNestedGenericsInEquality() throws ReflectiveOperationException {
ResolvableType containerList = ResolvableType.forField(WithContainer.class.getDeclaredField("containerList"));
ResolvableType containerMap = ResolvableType.forField(WithContainer.class.getDeclaredField("containerMap"));
assertThat(TypeInformation.of(containerList)).isNotEqualTo(TypeInformation.of(containerMap));
}
class Person {
Addresses addresses;
@@ -441,4 +451,15 @@ public class TypeDiscovererUnitTests {
class GeoResultsWrapper {
GeoResults<Leaf> results;
}
static class WithContainer {
MyContainer<List<String>> containerList;
MyContainer<List<Map<Long, Double>>> containerMap;
}
static class MyContainer<T> {
T data;
}
}