Skip to content

Commit 1ca941b

Browse files
committed
Consistently resolve renamed type variables
Closes gh-34386
1 parent 17a94fb commit 1ca941b

File tree

3 files changed

+43
-17
lines changed

3 files changed

+43
-17
lines changed

spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ public static Type resolveType(Type genericType, @Nullable Class<?> contextClass
169169
else if (genericType instanceof ParameterizedType parameterizedType) {
170170
ResolvableType resolvedType = ResolvableType.forType(genericType);
171171
if (resolvedType.hasUnresolvableGenerics()) {
172-
ResolvableType[] generics = new ResolvableType[parameterizedType.getActualTypeArguments().length];
173172
Type[] typeArguments = parameterizedType.getActualTypeArguments();
173+
ResolvableType[] generics = new ResolvableType[typeArguments.length];
174174
ResolvableType contextType = ResolvableType.forClass(contextClass);
175175
for (int i = 0; i < typeArguments.length; i++) {
176176
Type typeArgument = typeArguments[i];
@@ -209,6 +209,9 @@ private static ResolvableType resolveVariable(TypeVariable<?> typeVariable, Reso
209209
}
210210
resolvedType = variableResolver.resolveVariable(typeVariable);
211211
if (resolvedType != null) {
212+
while (resolvedType.getType() instanceof TypeVariable<?>) {
213+
resolvedType = resolvedType.resolveType();
214+
}
212215
return resolvedType;
213216
}
214217
}

spring-core/src/main/java/org/springframework/core/ResolvableType.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -964,10 +964,10 @@ private ResolvableType resolveVariable(TypeVariable<?> variable) {
964964
return null;
965965
}
966966
TypeVariable<?>[] variables = resolved.getTypeParameters();
967+
Type[] typeArguments = parameterizedType.getActualTypeArguments();
967968
for (int i = 0; i < variables.length; i++) {
968969
if (ObjectUtils.nullSafeEquals(variables[i].getName(), variable.getName())) {
969-
Type actualType = parameterizedType.getActualTypeArguments()[i];
970-
return forType(actualType, this.variableResolver);
970+
return forType(typeArguments[i], this.variableResolver);
971971
}
972972
}
973973
Type ownerType = parameterizedType.getOwnerType();

spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java

+37-14
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,25 @@
1818

1919
import java.io.Serializable;
2020
import java.lang.reflect.Method;
21+
import java.lang.reflect.ParameterizedType;
2122
import java.lang.reflect.Type;
2223
import java.lang.reflect.TypeVariable;
2324
import java.util.Collection;
2425
import java.util.HashMap;
2526
import java.util.List;
2627
import java.util.Map;
28+
import java.util.function.Supplier;
2729

2830
import org.junit.jupiter.api.Test;
2931

3032
import static org.assertj.core.api.Assertions.assertThat;
3133
import static org.springframework.core.GenericTypeResolver.getTypeVariableMap;
34+
import static org.springframework.core.GenericTypeResolver.resolveParameterType;
35+
import static org.springframework.core.GenericTypeResolver.resolveReturnType;
3236
import static org.springframework.core.GenericTypeResolver.resolveReturnTypeArgument;
3337
import static org.springframework.core.GenericTypeResolver.resolveType;
3438
import static org.springframework.core.GenericTypeResolver.resolveTypeArgument;
39+
import static org.springframework.core.GenericTypeResolver.resolveTypeArguments;
3540
import static org.springframework.util.ReflectionUtils.findMethod;
3641

3742
/**
@@ -106,25 +111,25 @@ void boundParameterizedType() {
106111
void testGetTypeVariableMap() {
107112
Map<TypeVariable, Type> map;
108113

109-
map = GenericTypeResolver.getTypeVariableMap(MySimpleInterfaceType.class);
114+
map = getTypeVariableMap(MySimpleInterfaceType.class);
110115
assertThat(map.toString()).isEqualTo("{T=class java.lang.String}");
111116

112-
map = GenericTypeResolver.getTypeVariableMap(MyCollectionInterfaceType.class);
117+
map = getTypeVariableMap(MyCollectionInterfaceType.class);
113118
assertThat(map.toString()).isEqualTo("{T=java.util.Collection<java.lang.String>}");
114119

115-
map = GenericTypeResolver.getTypeVariableMap(MyCollectionSuperclassType.class);
120+
map = getTypeVariableMap(MyCollectionSuperclassType.class);
116121
assertThat(map.toString()).isEqualTo("{T=java.util.Collection<java.lang.String>}");
117122

118-
map = GenericTypeResolver.getTypeVariableMap(MySimpleTypeWithMethods.class);
123+
map = getTypeVariableMap(MySimpleTypeWithMethods.class);
119124
assertThat(map.toString()).isEqualTo("{T=class java.lang.Integer}");
120125

121-
map = GenericTypeResolver.getTypeVariableMap(TopLevelClass.class);
126+
map = getTypeVariableMap(TopLevelClass.class);
122127
assertThat(map.toString()).isEqualTo("{}");
123128

124-
map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.class);
129+
map = getTypeVariableMap(TypedTopLevelClass.class);
125130
assertThat(map.toString()).isEqualTo("{T=class java.lang.Integer}");
126131

127-
map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.TypedNested.class);
132+
map = getTypeVariableMap(TypedTopLevelClass.TypedNested.class);
128133
assertThat(map).hasSize(2);
129134
Type t = null;
130135
Type x = null;
@@ -142,19 +147,19 @@ void testGetTypeVariableMap() {
142147

143148
@Test
144149
void resolveTypeArgumentsOfAbstractType() {
145-
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(MyConcreteType.class, MyAbstractType.class);
150+
Class<?>[] resolved = resolveTypeArguments(MyConcreteType.class, MyAbstractType.class);
146151
assertThat(resolved).containsExactly(Character.class);
147152
}
148153

149154
@Test // SPR-11030
150155
void getGenericsCannotBeResolved() {
151-
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class);
156+
Class<?>[] resolved = resolveTypeArguments(List.class, Iterable.class);
152157
assertThat(resolved).isNull();
153158
}
154159

155160
@Test // SPR-11052
156161
void getRawMapTypeCannotBeResolved() {
157-
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(Map.class, Map.class);
162+
Class<?>[] resolved = resolveTypeArguments(Map.class, Map.class);
158163
assertThat(resolved).isNull();
159164
}
160165

@@ -163,26 +168,38 @@ void getRawMapTypeCannotBeResolved() {
163168
void getGenericsOnArrayFromParamCannotBeResolved() throws Exception {
164169
MethodParameter methodParameter = MethodParameter.forExecutable(
165170
WithArrayBase.class.getDeclaredMethod("array", Object[].class), 0);
166-
Class<?> resolved = GenericTypeResolver.resolveParameterType(methodParameter, WithArray.class);
171+
Class<?> resolved = resolveParameterType(methodParameter, WithArray.class);
167172
assertThat(resolved).isEqualTo(Object[].class);
168173
}
169174

170175
@Test // SPR-11044
171176
void getGenericsOnArrayFromReturnCannotBeResolved() throws Exception {
172-
Class<?> resolved = GenericTypeResolver.resolveReturnType(
177+
Class<?> resolved = resolveReturnType(
173178
WithArrayBase.class.getDeclaredMethod("array", Object[].class), WithArray.class);
174179
assertThat(resolved).isEqualTo(Object[].class);
175180
}
176181

177182
@Test // SPR-11763
178183
void resolveIncompleteTypeVariables() {
179-
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(IdFixingRepository.class, Repository.class);
184+
Class<?>[] resolved = resolveTypeArguments(IdFixingRepository.class, Repository.class);
180185
assertThat(resolved).isNotNull();
181186
assertThat(resolved).hasSize(2);
182187
assertThat(resolved[0]).isEqualTo(Object.class);
183188
assertThat(resolved[1]).isEqualTo(Long.class);
184189
}
185190

191+
@Test // gh-34386
192+
void resolveVariableNameChange() {
193+
Type resolved = resolveType(Repository.class.getTypeParameters()[0], ConcreteRepository.class);
194+
assertThat(resolved).isEqualTo(String.class);
195+
Method method = method(Repository.class,"store", Supplier.class);
196+
resolved = resolveType(method.getGenericParameterTypes()[0], ConcreteRepository.class);
197+
assertThat(resolved).isInstanceOf(ParameterizedType.class);
198+
ParameterizedType pt = (ParameterizedType) resolved;
199+
assertThat(pt.getRawType()).isEqualTo(Supplier.class);
200+
assertThat(pt.getActualTypeArguments()[0]).isEqualTo(String.class);
201+
}
202+
186203
@Test
187204
void resolvePartiallySpecializedTypeVariables() {
188205
Type resolved = resolveType(BiGenericClass.class.getTypeParameters()[0], TypeFixedBiGenericClass.class);
@@ -398,9 +415,15 @@ abstract static class WithArray<T> extends WithArrayBase<T> {
398415
}
399416

400417
interface Repository<T, ID extends Serializable> {
418+
419+
default void store(Supplier<T> t) {
420+
}
421+
}
422+
423+
interface IdFixingRepository<V> extends Repository<V, Long> {
401424
}
402425

403-
interface IdFixingRepository<T> extends Repository<T, Long> {
426+
static class ConcreteRepository implements IdFixingRepository<String> {
404427
}
405428

406429
static class WithMethodParameter {

0 commit comments

Comments
 (0)