Skip to content

Commit 826e565

Browse files
committed
Polish GenericTypeResolver
- renamed resolveParameterizedReturnType() to resolveReturnTypeForGenericMethod() - fleshed out Javadoc for resolveReturnType() and resolveReturnTypeForGenericMethod() regarding declaration of formal type variables - improved wording in log statements and naming of local variables within resolveReturnTypeForGenericMethod() Issue: SPR-9493
1 parent 3fbcebb commit 826e565

File tree

4 files changed

+53
-54
lines changed

4 files changed

+53
-54
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ protected Class getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd,
646646
&& factoryMethod.getName().equals(mbd.getFactoryMethodName())
647647
&& factoryMethod.getParameterTypes().length >= minNrOfArgs) {
648648

649-
Class<?> returnType = GenericTypeResolver.resolveParameterizedReturnType(factoryMethod, args);
649+
Class<?> returnType = GenericTypeResolver.resolveReturnTypeForGenericMethod(factoryMethod, args);
650650
if (returnType != null) {
651651
returnTypes.add(returnType);
652652
}

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

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,12 @@ public static Class<?> resolveParameterType(MethodParameter methodParam, Class c
9696

9797
/**
9898
* Determine the target type for the generic return type of the given method,
99-
* where the type variable is declared on the given class.
99+
* where formal type variables are declared on the given class.
100100
*
101101
* @param method the method to introspect
102102
* @param clazz the class to resolve type variables against
103103
* @return the corresponding generic parameter or return type
104-
* @see #resolveParameterizedReturnType
104+
* @see #resolveReturnTypeForGenericMethod
105105
*/
106106
public static Class<?> resolveReturnType(Method method, Class<?> clazz) {
107107
Assert.notNull(method, "Method must not be null");
@@ -114,27 +114,27 @@ public static Class<?> resolveReturnType(Method method, Class<?> clazz) {
114114

115115
/**
116116
* Determine the target type for the generic return type of the given
117-
* <em>parameterized</em> method, where the type variable is declared
118-
* on the given method.
117+
* <em>generic method</em>, where formal type variables are declared on
118+
* the given method itself.
119119
*
120120
* <p>For example, given a factory method with the following signature,
121-
* if {@code resolveParameterizedReturnType()} is invoked with the reflected
121+
* if {@code resolveReturnTypeForGenericMethod()} is invoked with the reflected
122122
* method for {@code creatProxy()} and an {@code Object[]} array containing
123-
* {@code MyService.class}, {@code resolveParameterizedReturnType()} will
123+
* {@code MyService.class}, {@code resolveReturnTypeForGenericMethod()} will
124124
* infer that the target return type is {@code MyService}.
125125
*
126126
* <pre>{@code public static <T> T createProxy(Class<T> clazz)}</pre>
127127
*
128128
* <h4>Possible Return Values</h4>
129129
* <ul>
130-
* <li>the target return type if it can be inferred</li>
131-
* <li>the {@link Method#getReturnType() standard return type}, if
132-
* the given {@code method} does not declare any {@link
133-
* Method#getTypeParameters() generic types}</li>
134-
* <li>the {@link Method#getReturnType() standard return type}, if the
130+
* <li>the target return type, if it can be inferred</li>
131+
* <li>the {@linkplain Method#getReturnType() standard return type}, if
132+
* the given {@code method} does not declare any {@linkplain
133+
* Method#getTypeParameters() formal type variables}</li>
134+
* <li>the {@linkplain Method#getReturnType() standard return type}, if the
135135
* target return type cannot be inferred (e.g., due to type erasure)</li>
136136
* <li>{@code null}, if the length of the given arguments array is shorter
137-
* than the length of the {@link
137+
* than the length of the {@linkplain
138138
* Method#getGenericParameterTypes() formal argument list} for the given
139139
* method</li>
140140
* </ul>
@@ -147,60 +147,59 @@ public static Class<?> resolveReturnType(Method method, Class<?> clazz) {
147147
* @since 3.2
148148
* @see #resolveReturnType
149149
*/
150-
public static Class<?> resolveParameterizedReturnType(Method method, Object[] args) {
150+
public static Class<?> resolveReturnTypeForGenericMethod(Method method, Object[] args) {
151151
Assert.notNull(method, "method must not be null");
152152
Assert.notNull(args, "args must not be null");
153153

154-
final TypeVariable<Method>[] declaredGenericTypes = method.getTypeParameters();
155-
final Type genericReturnType = method.getGenericReturnType();
156-
final Type[] genericArgumentTypes = method.getGenericParameterTypes();
157-
158154
if (logger.isDebugEnabled()) {
159-
logger.debug(String.format(
160-
"Resolving parameterized return type for [%s] with concrete method arguments [%s].",
155+
logger.debug(String.format("Resolving return type for [%s] with concrete method arguments [%s].",
161156
method.toGenericString(), ObjectUtils.nullSafeToString(args)));
162157
}
163158

164-
// No declared generic types to inspect, so just return the standard return type.
165-
if (declaredGenericTypes.length == 0) {
159+
final TypeVariable<Method>[] declaredTypeVariables = method.getTypeParameters();
160+
final Type genericReturnType = method.getGenericReturnType();
161+
final Type[] methodArgumentTypes = method.getGenericParameterTypes();
162+
163+
// No declared type variables to inspect, so just return the standard return type.
164+
if (declaredTypeVariables.length == 0) {
166165
return method.getReturnType();
167166
}
168167

169168
// The supplied argument list is too short for the method's signature, so
170169
// return null, since such a method invocation would fail.
171-
if (args.length < genericArgumentTypes.length) {
170+
if (args.length < methodArgumentTypes.length) {
172171
return null;
173172
}
174173

175-
// Ensure that the generic type is declared directly on the method
176-
// itself, not on the enclosing class or interface.
177-
boolean locallyDeclaredGenericTypeMatchesReturnType = false;
178-
for (TypeVariable<Method> currentType : declaredGenericTypes) {
179-
if (currentType.equals(genericReturnType)) {
174+
// Ensure that the type variable (e.g., T) is declared directly on the method
175+
// itself (e.g., via <T>), not on the enclosing class or interface.
176+
boolean locallyDeclaredTypeVariableMatchesReturnType = false;
177+
for (TypeVariable<Method> currentTypeVariable : declaredTypeVariables) {
178+
if (currentTypeVariable.equals(genericReturnType)) {
180179
if (logger.isDebugEnabled()) {
181180
logger.debug(String.format(
182-
"Found declared generic type [%s] that matches the target return type [%s].",
183-
currentType, genericReturnType));
181+
"Found declared type variable [%s] that matches the target return type [%s].",
182+
currentTypeVariable, genericReturnType));
184183
}
185-
locallyDeclaredGenericTypeMatchesReturnType = true;
184+
locallyDeclaredTypeVariableMatchesReturnType = true;
186185
break;
187186
}
188187
}
189188

190-
if (locallyDeclaredGenericTypeMatchesReturnType) {
191-
for (int i = 0; i < genericArgumentTypes.length; i++) {
192-
final Type currentArgumentType = genericArgumentTypes[i];
189+
if (locallyDeclaredTypeVariableMatchesReturnType) {
190+
for (int i = 0; i < methodArgumentTypes.length; i++) {
191+
final Type currentMethodArgumentType = methodArgumentTypes[i];
193192

194-
if (currentArgumentType.equals(genericReturnType)) {
193+
if (currentMethodArgumentType.equals(genericReturnType)) {
195194
if (logger.isDebugEnabled()) {
196195
logger.debug(String.format(
197-
"Found generic method argument at index [%s] that matches the target return type.", i));
196+
"Found method argument type at index [%s] that matches the target return type.", i));
198197
}
199198
return args[i].getClass();
200199
}
201200

202-
if (currentArgumentType instanceof ParameterizedType) {
203-
ParameterizedType parameterizedType = (ParameterizedType) currentArgumentType;
201+
if (currentMethodArgumentType instanceof ParameterizedType) {
202+
ParameterizedType parameterizedType = (ParameterizedType) currentMethodArgumentType;
204203
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
205204

206205
for (int j = 0; j < actualTypeArguments.length; j++) {
@@ -209,7 +208,7 @@ public static Class<?> resolveParameterizedReturnType(Method method, Object[] ar
209208
if (typeArg.equals(genericReturnType)) {
210209
if (logger.isDebugEnabled()) {
211210
logger.debug(String.format(
212-
"Found method argument at index [%s] that is parameterized with a type that matches the target return type.",
211+
"Found method argument type at index [%s] that is parameterized with a type argument that matches the target return type.",
213212
i));
214213
}
215214

@@ -219,7 +218,7 @@ public static Class<?> resolveParameterizedReturnType(Method method, Object[] ar
219218
// Consider adding logic to determine the class of the
220219
// J'th typeArg, if possible.
221220
logger.info(String.format(
222-
"Could not determine the target type for parameterized type [%s] for method [%s].",
221+
"Could not determine the target type for type argument [%s] for method [%s].",
223222
typeArg, method.toGenericString()));
224223

225224
// For now, just fall back...

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,59 +73,59 @@ public void methodReturnTypes() {
7373
* @since 3.2
7474
*/
7575
@Test
76-
public void parameterizedMethodReturnTypes() {
76+
public void genericMethodReturnTypes() {
7777

7878
Method notParameterized = findMethod(MyTypeWithMethods.class, "notParameterized", new Class[] {});
79-
assertEquals(String.class, resolveParameterizedReturnType(notParameterized, new Object[] {}));
79+
assertEquals(String.class, resolveReturnTypeForGenericMethod(notParameterized, new Object[] {}));
8080

8181
Method notParameterizedWithArguments = findMethod(MyTypeWithMethods.class, "notParameterizedWithArguments",
8282
new Class[] { Integer.class, Boolean.class });
8383
assertEquals(String.class,
84-
resolveParameterizedReturnType(notParameterizedWithArguments, new Object[] { 99, true }));
84+
resolveReturnTypeForGenericMethod(notParameterizedWithArguments, new Object[] { 99, true }));
8585

8686
Method createProxy = findMethod(MyTypeWithMethods.class, "createProxy", new Class[] { Object.class });
87-
assertEquals(String.class, resolveParameterizedReturnType(createProxy, new Object[] { "foo" }));
87+
assertEquals(String.class, resolveReturnTypeForGenericMethod(createProxy, new Object[] { "foo" }));
8888

8989
Method createNamedProxyWithDifferentTypes = findMethod(MyTypeWithMethods.class, "createNamedProxy",
9090
new Class[] { String.class, Object.class });
9191
// one argument to few
92-
assertNull(resolveParameterizedReturnType(createNamedProxyWithDifferentTypes, new Object[] { "enigma" }));
92+
assertNull(resolveReturnTypeForGenericMethod(createNamedProxyWithDifferentTypes, new Object[] { "enigma" }));
9393
assertEquals(Long.class,
94-
resolveParameterizedReturnType(createNamedProxyWithDifferentTypes, new Object[] { "enigma", 99L }));
94+
resolveReturnTypeForGenericMethod(createNamedProxyWithDifferentTypes, new Object[] { "enigma", 99L }));
9595

9696
Method createNamedProxyWithDuplicateTypes = findMethod(MyTypeWithMethods.class, "createNamedProxy",
9797
new Class[] { String.class, Object.class });
9898
assertEquals(String.class,
99-
resolveParameterizedReturnType(createNamedProxyWithDuplicateTypes, new Object[] { "enigma", "foo" }));
99+
resolveReturnTypeForGenericMethod(createNamedProxyWithDuplicateTypes, new Object[] { "enigma", "foo" }));
100100

101101
Method createMock = findMethod(MyTypeWithMethods.class, "createMock", new Class[] { Class.class });
102-
assertEquals(Runnable.class, resolveParameterizedReturnType(createMock, new Object[] { Runnable.class }));
102+
assertEquals(Runnable.class, resolveReturnTypeForGenericMethod(createMock, new Object[] { Runnable.class }));
103103

104104
Method createNamedMock = findMethod(MyTypeWithMethods.class, "createNamedMock", new Class[] { String.class,
105105
Class.class });
106106
assertEquals(Runnable.class,
107-
resolveParameterizedReturnType(createNamedMock, new Object[] { "foo", Runnable.class }));
107+
resolveReturnTypeForGenericMethod(createNamedMock, new Object[] { "foo", Runnable.class }));
108108

109109
Method createVMock = findMethod(MyTypeWithMethods.class, "createVMock",
110110
new Class[] { Object.class, Class.class });
111111
assertEquals(Runnable.class,
112-
resolveParameterizedReturnType(createVMock, new Object[] { "foo", Runnable.class }));
112+
resolveReturnTypeForGenericMethod(createVMock, new Object[] { "foo", Runnable.class }));
113113

114114
// Ideally we would expect String.class instead of Object.class, but
115-
// resolveParameterizedReturnType() does not currently support this form of
115+
// resolveReturnTypeForGenericMethod() does not currently support this form of
116116
// look-up.
117117
Method extractValueFrom = findMethod(MyTypeWithMethods.class, "extractValueFrom",
118118
new Class[] { MyInterfaceType.class });
119119
assertEquals(Object.class,
120-
resolveParameterizedReturnType(extractValueFrom, new Object[] { new MySimpleInterfaceType() }));
120+
resolveReturnTypeForGenericMethod(extractValueFrom, new Object[] { new MySimpleInterfaceType() }));
121121

122122
// Ideally we would expect Boolean.class instead of Object.class, but this
123123
// information is not available at run-time due to type erasure.
124124
Map<Integer, Boolean> map = new HashMap<Integer, Boolean>();
125125
map.put(0, false);
126126
map.put(1, true);
127127
Method extractMagicValue = findMethod(MyTypeWithMethods.class, "extractMagicValue", new Class[] { Map.class });
128-
assertEquals(Object.class, resolveParameterizedReturnType(extractMagicValue, new Object[] { map }));
128+
assertEquals(Object.class, resolveReturnTypeForGenericMethod(extractMagicValue, new Object[] { map }));
129129
}
130130

131131

src/dist/changelog.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Changes in version 3.2 M2 (2012-08-xx)
77
--------------------------------------
88

99
* spring-test module now depends on junit:junit-dep (SPR-6966)
10-
* now inferring return type of parameterized factory methods (SPR-9493)
10+
* now inferring return type of generic factory methods (SPR-9493)
1111
* SpEL Tokenizer now supports methods on integers (SPR-9612)
1212
* introduced support for case-insensitive null literals in SpEL expressions (SPR-9613)
1313
* now using BufferedInputStream in SimpleMetaDataReader to double performance (SPR-9528)

0 commit comments

Comments
 (0)