diff --git a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
index 886549630a8..b860aaafca8 100644
--- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
+++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
@@ -231,7 +231,7 @@ public abstract class GenericTypeResolver {
* @return the resolved type of the argument, or {@code null} if not resolvable
*/
public static Class> resolveTypeArgument(Class> clazz, Class> genericIfc) {
- Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
+ Class>[] typeArgs = resolveTypeArguments(clazz, genericIfc);
if (typeArgs == null) {
return null;
}
@@ -246,21 +246,25 @@ public abstract class GenericTypeResolver {
* Resolve the type arguments of the given generic interface against the given
* target class which is assumed to implement the generic interface and possibly
* declare concrete types for its type variables.
+ *
Note: In Spring 3.2, this method doesn't return {@code null} in all scenarios
+ * where it should. To be fixed in Spring 4.0; for client code, this just means it
+ * might see {@code null} in a few more cases then where it now sees an array with
+ * a single {@link Object} type.
* @param clazz the target class to check against
* @param genericIfc the generic interface or superclass to resolve the type argument from
* @return the resolved type of each argument, with the array size matching the
* number of actual type arguments, or {@code null} if not resolvable
*/
- public static Class[] resolveTypeArguments(Class> clazz, Class> genericIfc) {
+ public static Class>[] resolveTypeArguments(Class> clazz, Class> genericIfc) {
return doResolveTypeArguments(clazz, clazz, genericIfc);
}
- private static Class[] doResolveTypeArguments(Class> ownerClass, Class> classToIntrospect, Class> genericIfc) {
+ private static Class>[] doResolveTypeArguments(Class> ownerClass, Class> classToIntrospect, Class> genericIfc) {
while (classToIntrospect != null) {
if (genericIfc.isInterface()) {
Type[] ifcs = classToIntrospect.getGenericInterfaces();
for (Type ifc : ifcs) {
- Class[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
+ Class>[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
if (result != null) {
return result;
}
@@ -268,7 +272,7 @@ public abstract class GenericTypeResolver {
}
else {
try {
- Class[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
+ Class>[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
if (result != null) {
return result;
}
@@ -283,13 +287,13 @@ public abstract class GenericTypeResolver {
return null;
}
- private static Class[] doResolveTypeArguments(Class> ownerClass, Type ifc, Class> genericIfc) {
+ private static Class>[] doResolveTypeArguments(Class> ownerClass, Type ifc, Class> genericIfc) {
if (ifc instanceof ParameterizedType) {
ParameterizedType paramIfc = (ParameterizedType) ifc;
Type rawType = paramIfc.getRawType();
if (genericIfc.equals(rawType)) {
Type[] typeArgs = paramIfc.getActualTypeArguments();
- Class[] result = new Class[typeArgs.length];
+ Class>[] result = new Class[typeArgs.length];
for (int i = 0; i < typeArgs.length; i++) {
Type arg = typeArgs[i];
result[i] = extractClass(ownerClass, arg);
diff --git a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
index 9f7d37b3d73..ca335500027 100644
--- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
+++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,10 +21,12 @@ import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.junit.Test;
+import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.core.GenericTypeResolver.*;
import static org.springframework.util.ReflectionUtils.*;
@@ -63,28 +65,33 @@ public class GenericTypeResolverTests {
@Test
public void methodReturnTypes() {
- assertEquals(Integer.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
- assertEquals(String.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
+ assertEquals(Integer.class,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
+ assertEquals(String.class,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "raw"), MyInterfaceType.class));
- assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
+ assertEquals(null,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
}
@Test
public void testResolveType() {
- Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
- MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
- assertEquals(MyInterfaceType.class,
- resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap()));
-
- Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage", MyInterfaceType[].class);
- MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
- assertEquals(MyInterfaceType[].class,
- resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap()));
-
- Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage", Object[].class);
- MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
- Map varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
- assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
+ Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
+ MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
+ assertEquals(MyInterfaceType.class,
+ resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap()));
+
+ Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage",
+ MyInterfaceType[].class);
+ MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
+ assertEquals(MyInterfaceType[].class,
+ resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap()));
+
+ Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage",
+ Object[].class);
+ MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
+ Map varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
+ assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
}
@Test
@@ -92,7 +99,76 @@ public class GenericTypeResolverTests {
assertEquals(B.class, resolveTypeArgument(TestImpl.class, ITest.class));
}
+ @Test
+ public void testGetTypeVariableMap() throws Exception {
+ Map map;
+
+ map = GenericTypeResolver.getTypeVariableMap(MySimpleInterfaceType.class);
+ assertThat(map.toString(), equalTo("{T=class java.lang.String}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(MyCollectionInterfaceType.class);
+ assertThat(map.toString(), equalTo("{T=java.util.Collection}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(MyCollectionSuperclassType.class);
+ assertThat(map.toString(), equalTo("{T=java.util.Collection}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(MySimpleTypeWithMethods.class);
+ assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(TopLevelClass.class);
+ assertThat(map.toString(), equalTo("{}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.class);
+ assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
+
+ map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.TypedNested.class);
+ assertThat(map.size(), equalTo(2));
+ Type t = null;
+ Type x = null;
+ for (Map.Entry entry : map.entrySet()) {
+ if(entry.getKey().toString().equals("T")) {
+ t = entry.getValue();
+ }
+ else {
+ x = entry.getValue();
+ }
+ }
+ assertThat(t, equalTo((Type) Integer.class));
+ assertThat(x, equalTo((Type) Long.class));
+ }
+
+ @Test
+ public void getGenericsCannotBeResolved() throws Exception {
+ // SPR-11030
+ Class>[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class);
+ // Note: to be changed to return null in Spring 4.0
+ assertThat(resolved, equalTo(new Class[] {Object.class}));
+ }
+ @Test
+ public void getRawMapTypeCannotBeResolved() throws Exception {
+ // SPR-11052
+ Class>[] resolved = GenericTypeResolver.resolveTypeArguments(Map.class, Map.class);
+ assertNull(resolved);
+ }
+
+ @Test
+ public void getGenericsOnArrayFromParamCannotBeResolved() throws Exception {
+ // SPR-11044
+ MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(
+ WithArrayBase.class.getDeclaredMethod("array", Object[].class), 0);
+ Class> resolved = GenericTypeResolver.resolveParameterType(methodParameter, WithArray.class);
+ assertThat(resolved, equalTo((Class) Object[].class));
+ }
+
+ @Test
+ public void getGenericsOnArrayFromReturnCannotBeResolved() throws Exception {
+ // SPR-11044
+ Class> resolved = GenericTypeResolver.resolveReturnType(
+ WithArrayBase.class.getDeclaredMethod("array", Object[].class),
+ WithArray.class);
+ assertThat(resolved, equalTo((Class) Object[].class));
+ }
public interface MyInterfaceType {
}
@@ -113,26 +189,44 @@ public class GenericTypeResolverTests {
}
public static class MyTypeWithMethods {
- public MyInterfaceType integer() { return null; }
- public MySimpleInterfaceType string() { return null; }
- public Object object() { return null; }
+
+ public MyInterfaceType integer() {
+ return null;
+ }
+
+ public MySimpleInterfaceType string() {
+ return null;
+ }
+
+ public Object object() {
+ return null;
+ }
+
@SuppressWarnings("rawtypes")
- public MyInterfaceType raw() { return null; }
- public String notParameterized() { return null; }
- public String notParameterizedWithArguments(Integer x, Boolean b) { return null; }
+ public MyInterfaceType raw() {
+ return null;
+ }
+
+ public String notParameterized() {
+ return null;
+ }
+
+ public String notParameterizedWithArguments(Integer x, Boolean b) {
+ return null;
+ }
/**
- * Simulates a factory method that wraps the supplied object in a proxy
- * of the same type.
+ * Simulates a factory method that wraps the supplied object in a proxy of the
+ * same type.
*/
public static T createProxy(T object) {
return null;
}
/**
- * Similar to {@link #createProxy(Object)} but adds an additional argument
- * before the argument of type {@code T}. Note that they may potentially
- * be of the same time when invoked!
+ * Similar to {@link #createProxy(Object)} but adds an additional argument before
+ * the argument of type {@code T}. Note that they may potentially be of the same
+ * time when invoked!
*/
public static T createNamedProxy(String name, T object) {
return null;
@@ -146,8 +240,8 @@ public class GenericTypeResolverTests {
}
/**
- * Similar to {@link #createMock(Class)} but adds an additional method
- * argument before the parameterized argument.
+ * Similar to {@link #createMock(Class)} but adds an additional method argument
+ * before the parameterized argument.
*/
public static T createNamedMock(String name, Class toMock) {
return null;
@@ -162,8 +256,8 @@ public class GenericTypeResolverTests {
}
/**
- * Extract some value of the type supported by the interface (i.e., by
- * a concrete, non-generic implementation of the interface).
+ * Extract some value of the type supported by the interface (i.e., by a concrete,
+ * non-generic implementation of the interface).
*/
public static T extractValueFrom(MyInterfaceType myInterfaceType) {
return null;
@@ -201,4 +295,22 @@ public class GenericTypeResolverTests {
class TestImpl> extends ITest{
}
+ static class TopLevelClass {
+ class Nested {
+ }
+ }
+
+ static class TypedTopLevelClass extends TopLevelClass {
+ class TypedNested extends Nested {
+ }
+ }
+
+ static abstract class WithArrayBase {
+
+ public abstract T[] array(T... args);
+ }
+
+ static abstract class WithArray extends WithArrayBase {
+ }
+
}