Skip to content

Commit 43caf49

Browse files
committed
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
1 parent 1380988 commit 43caf49

File tree

2 files changed

+28
-9
lines changed

2 files changed

+28
-9
lines changed

src/main/java/org/springframework/data/util/TypeDiscoverer.java

+7-9
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
6060
private final Map<Constructor<?>, List<TypeInformation<?>>> constructorParameters = new ConcurrentHashMap<>();
6161
private final Lazy<List<TypeInformation<?>>> typeArguments;
6262

63+
private final Lazy<List<TypeInformation<?>>> resolvedGenerics;
64+
6365
protected TypeDiscoverer(ResolvableType type) {
6466

6567
Assert.notNull(type, "Type must not be null");
@@ -68,6 +70,10 @@ protected TypeDiscoverer(ResolvableType type) {
6870
this.componentType = Lazy.of(this::doGetComponentType);
6971
this.valueType = Lazy.of(this::doGetMapValueType);
7072
this.typeArguments = Lazy.of(this::doGetTypeArguments);
73+
this.resolvedGenerics = Lazy.of(() -> Arrays.stream(resolvableType.getGenerics()) //
74+
.map(TypeInformation::of) // use TypeInformation comparison to remove any attachments to variableResolver
75+
// holding the type source
76+
.collect(Collectors.toList()));
7177
}
7278

7379
static TypeDiscoverer<?> td(ResolvableType type) {
@@ -325,15 +331,7 @@ public boolean equals(@Nullable Object o) {
325331
return false;
326332
}
327333

328-
var collect1 = Arrays.stream(resolvableType.getGenerics()) //
329-
.map(ResolvableType::toClass) //
330-
.collect(Collectors.toList());
331-
332-
var collect2 = Arrays.stream(that.resolvableType.getGenerics()) //
333-
.map(ResolvableType::toClass) //
334-
.collect(Collectors.toList());
335-
336-
return ObjectUtils.nullSafeEquals(collect1, collect2);
334+
return ObjectUtils.nullSafeEquals(resolvedGenerics.get(), that.resolvedGenerics.get());
337335
}
338336

339337
@Override

src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java

+21
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.junit.jupiter.api.Test;
3030
import org.junit.jupiter.api.extension.ExtendWith;
3131
import org.mockito.junit.jupiter.MockitoExtension;
32+
3233
import org.springframework.beans.factory.annotation.Autowire;
3334
import org.springframework.core.ResolvableType;
3435
import org.springframework.data.geo.GeoResults;
@@ -348,6 +349,15 @@ void differentEqualsAndHashCodeForTypeDiscovererAndClassTypeInformation() {
348349
assertThat(discoverer.hashCode()).isNotEqualTo(classTypeInformation.hashCode());
349350
}
350351

352+
@Test // GH-3051
353+
void considersNestedGenericsInEquality() throws ReflectiveOperationException {
354+
355+
ResolvableType containerList = ResolvableType.forField(WithContainer.class.getDeclaredField("containerList"));
356+
ResolvableType containerMap = ResolvableType.forField(WithContainer.class.getDeclaredField("containerMap"));
357+
358+
assertThat(TypeInformation.of(containerList)).isNotEqualTo(TypeInformation.of(containerMap));
359+
}
360+
351361
class Person {
352362

353363
Addresses addresses;
@@ -441,4 +451,15 @@ class EnumMapWrapper {
441451
class GeoResultsWrapper {
442452
GeoResults<Leaf> results;
443453
}
454+
455+
static class WithContainer {
456+
MyContainer<List<String>> containerList;
457+
458+
MyContainer<List<Map<Long, Double>>> containerMap;
459+
}
460+
461+
static class MyContainer<T> {
462+
T data;
463+
}
464+
444465
}

0 commit comments

Comments
 (0)