Skip to content

Commit c64558c

Browse files
committed
Retain subclass fields over masked superclass fields in TypeDiscoverer.
When a class hides a superclass field by declaring a field of the same name, property discovery overwrote the property type from the superclass field instead of retaining the declaring subclass field. This happened because ReflectionUtils.doWithFields traverses fields from the leaf type up through its superclasses, and the field callback unconditionally put each field into the result map. As a result, the superclass field, visited last, overwrote the entry contributed by the subclass, exposing the wrong (and potentially incompatible) property type. We now keep the first field encountered for a given name, which is the one declared closest to the inspected type, so a hidden superclass field no longer overrides the subclass declaration. Closes #3500 Original pull request: #3504
1 parent 3c60951 commit c64558c

2 files changed

Lines changed: 23 additions & 2 deletions

File tree

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,11 @@ private Map<String, TypeInformation<?>> doGetProperties() {
407407

408408
Map<String, TypeInformation<?>> result = new HashMap<>();
409409
Class<?> type = getType();
410-
FieldCallback callback = field -> result.put(field.getName(),
411-
TypeInformation.of(ResolvableType.forField(field, resolvableType)));
410+
FieldCallback callback = field -> {
411+
if (!result.containsKey(field.getName())) {
412+
result.put(field.getName(), TypeInformation.of(ResolvableType.forField(field, resolvableType)));
413+
}
414+
};
412415

413416
// Inspect fields first
414417
ReflectionUtils.doWithFields(type, callback);

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,14 @@ void considersNestedGenericsInEqualityForRecursiveUnresolvableTypes() throws Exc
379379
assertThat(domainType.isAssignableFrom(actualType)).isTrue();
380380
}
381381

382+
@Test // GH-3500
383+
void superclassFieldsDoNotHideSubclassFields() {
384+
385+
TypeDiscoverer<?> discoverer = TypeDiscoverer.ofCached(ResolvableType.forClass(TheOne.class));
386+
387+
assertThat(discoverer.getProperty("theOne").getType()).isEqualTo(String.class);
388+
}
389+
382390
class Person {
383391

384392
Addresses addresses;
@@ -487,6 +495,16 @@ static abstract class SomeType<Self extends SomeType<Self>> {
487495

488496
}
489497

498+
static class SuperclassWithProperties {
499+
private CharSequence theOne;
500+
private String theOther;
501+
}
502+
503+
class TheOne extends SuperclassWithProperties {
504+
private String theOne;
505+
}
506+
507+
490508
@SuppressWarnings("rawtypes")
491509
interface RepoWithRawGenerics extends Repository<SomeType, SomeType> {
492510

0 commit comments

Comments
 (0)