Merge pull request #97 from sbrannen/SPR-9493
* SPR-9493: Infer return type of parametrized factory methods
This commit is contained in:
commit
64d6605974
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
|
@ -63,6 +63,7 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
|||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
|
||||
|
@ -106,6 +107,7 @@ import org.springframework.util.StringUtils;
|
|||
* @author Mark Fisher
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @since 13.02.2004
|
||||
* @see RootBeanDefinition
|
||||
* @see DefaultListableBeanFactory
|
||||
|
@ -628,16 +630,26 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
return null;
|
||||
}
|
||||
|
||||
List<ValueHolder> argumentValues = mbd.getConstructorArgumentValues().getGenericArgumentValues();
|
||||
Object[] args = new Object[argumentValues.size()];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
args[i] = argumentValues.get(i).getValue();
|
||||
}
|
||||
|
||||
// If all factory methods have the same return type, return that type.
|
||||
// Can't clearly figure out exact method due to type converting / autowiring!
|
||||
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
|
||||
Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(factoryClass);
|
||||
Set<Class> returnTypes = new HashSet<Class>(1);
|
||||
Set<Class<?>> returnTypes = new HashSet<Class<?>>(1);
|
||||
for (Method factoryMethod : candidates) {
|
||||
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
|
||||
factoryMethod.getName().equals(mbd.getFactoryMethodName()) &&
|
||||
factoryMethod.getParameterTypes().length >= minNrOfArgs) {
|
||||
returnTypes.add(factoryMethod.getReturnType());
|
||||
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic
|
||||
&& factoryMethod.getName().equals(mbd.getFactoryMethodName())
|
||||
&& factoryMethod.getParameterTypes().length >= minNrOfArgs) {
|
||||
|
||||
Class<?> returnType = GenericTypeResolver.resolveParameterizedReturnType(factoryMethod, args);
|
||||
if (returnType != null) {
|
||||
returnTypes.add(returnType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
|
@ -29,6 +29,8 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
import org.junit.Test;
|
||||
import test.beans.GenericBean;
|
||||
import test.beans.GenericIntegerBean;
|
||||
|
@ -46,6 +48,7 @@ import org.springframework.core.io.UrlResource;
|
|||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @since 20.01.2006
|
||||
*/
|
||||
public class BeanFactoryGenericsTests {
|
||||
|
@ -619,6 +622,30 @@ public class BeanFactoryGenericsTests {
|
|||
assertEquals(new URL("http://www.springframework.org"), us.iterator().next());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests support for parameterized {@code factory-method} declarations such
|
||||
* as EasyMock's {@code createMock()} method which has the following signature.
|
||||
*
|
||||
* <pre>{@code
|
||||
* public static <T> T createMock(Class<T> toMock)
|
||||
* }</pre>
|
||||
*
|
||||
* @since 3.2
|
||||
* @see SPR-9493
|
||||
*/
|
||||
@Test
|
||||
public void parameterizedFactoryMethod() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(EasyMock.class);
|
||||
rbd.setFactoryMethodName("createMock");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
|
||||
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerBeanDefinition("easyMock", rbd);
|
||||
|
||||
Map<String, Runnable> beans = bf.getBeansOfType(Runnable.class);
|
||||
assertEquals(1, beans.size());
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class NamedUrlList extends LinkedList<URL> {
|
||||
|
|
|
@ -28,7 +28,6 @@ import test.beans.TestBean;
|
|||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
<level value="debug" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.core.GenericTypeResolver">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<!-- Root Logger -->
|
||||
<root>
|
||||
<priority value="warn" />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
|
@ -30,7 +30,11 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Helper class for resolving generic types against type variables.
|
||||
|
@ -40,11 +44,14 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Sam Brannen
|
||||
* @since 2.5.2
|
||||
* @see GenericCollectionTypeResolver
|
||||
*/
|
||||
public abstract class GenericTypeResolver {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GenericTypeResolver.class);
|
||||
|
||||
/** Cache from Class to TypeVariable Map */
|
||||
private static final Map<Class, Reference<Map<TypeVariable, Type>>> typeVariableCache =
|
||||
Collections.synchronizedMap(new WeakHashMap<Class, Reference<Map<TypeVariable, Type>>>());
|
||||
|
@ -88,18 +95,144 @@ public abstract class GenericTypeResolver {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determine the target type for the generic return type of the given method.
|
||||
* Determine the target type for the generic return type of the given method,
|
||||
* where the type variable is declared on the given class.
|
||||
*
|
||||
* @param method the method to introspect
|
||||
* @param clazz the class to resolve type variables against
|
||||
* @return the corresponding generic parameter or return type
|
||||
* @see #resolveParameterizedReturnType
|
||||
*/
|
||||
public static Class<?> resolveReturnType(Method method, Class clazz) {
|
||||
public static Class<?> resolveReturnType(Method method, Class<?> clazz) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
Type genericType = method.getGenericReturnType();
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
|
||||
Type rawType = getRawType(genericType, typeVariableMap);
|
||||
return (rawType instanceof Class ? (Class) rawType : method.getReturnType());
|
||||
return (rawType instanceof Class ? (Class<?>) rawType : method.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the target type for the generic return type of the given
|
||||
* <em>parameterized</em> method, where the type variable is declared
|
||||
* on the given method.
|
||||
*
|
||||
* <p>For example, given a factory method with the following signature,
|
||||
* if {@code resolveParameterizedReturnType()} is invoked with the reflected
|
||||
* method for {@code creatProxy()} and an {@code Object[]} array containing
|
||||
* {@code MyService.class}, {@code resolveParameterizedReturnType()} will
|
||||
* infer that the target return type is {@code MyService}.
|
||||
*
|
||||
* <pre>{@code public static <T> T createProxy(Class<T> clazz)}</pre>
|
||||
*
|
||||
* <h4>Possible Return Values</h4>
|
||||
* <ul>
|
||||
* <li>the target return type if it can be inferred</li>
|
||||
* <li>the {@link Method#getReturnType() standard return type}, if
|
||||
* the given {@code method} does not declare any {@link
|
||||
* Method#getTypeParameters() generic types}</li>
|
||||
* <li>the {@link Method#getReturnType() standard return type}, if the
|
||||
* target return type cannot be inferred (e.g., due to type erasure)</li>
|
||||
* <li>{@code null}, if the length of the given arguments array is shorter
|
||||
* than the length of the {@link
|
||||
* Method#getGenericParameterTypes() formal argument list} for the given
|
||||
* method</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param method the method to introspect, never {@code null}
|
||||
* @param args the arguments that will be supplied to the method when it is
|
||||
* invoked, never {@code null}
|
||||
* @return the resolved target return type, the standard return type, or
|
||||
* {@code null}
|
||||
* @since 3.2
|
||||
* @see #resolveReturnType
|
||||
*/
|
||||
public static Class<?> resolveParameterizedReturnType(Method method, Object[] args) {
|
||||
Assert.notNull(method, "method must not be null");
|
||||
Assert.notNull(args, "args must not be null");
|
||||
|
||||
final TypeVariable<Method>[] declaredGenericTypes = method.getTypeParameters();
|
||||
final Type genericReturnType = method.getGenericReturnType();
|
||||
final Type[] genericArgumentTypes = method.getGenericParameterTypes();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Resolving parameterized return type for [%s] with concrete method arguments [%s].",
|
||||
method.toGenericString(), ObjectUtils.nullSafeToString(args)));
|
||||
}
|
||||
|
||||
// No declared generic types to inspect, so just return the standard return type.
|
||||
if (declaredGenericTypes.length == 0) {
|
||||
return method.getReturnType();
|
||||
}
|
||||
|
||||
// The supplied argument list is too short for the method's signature, so
|
||||
// return null, since such a method invocation would fail.
|
||||
if (args.length < genericArgumentTypes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure that the generic type is declared directly on the method
|
||||
// itself, not on the enclosing class or interface.
|
||||
boolean locallyDeclaredGenericTypeMatchesReturnType = false;
|
||||
for (TypeVariable<Method> currentType : declaredGenericTypes) {
|
||||
if (currentType.equals(genericReturnType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Found declared generic type [%s] that matches the target return type [%s].",
|
||||
currentType, genericReturnType));
|
||||
}
|
||||
locallyDeclaredGenericTypeMatchesReturnType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (locallyDeclaredGenericTypeMatchesReturnType) {
|
||||
for (int i = 0; i < genericArgumentTypes.length; i++) {
|
||||
final Type currentArgumentType = genericArgumentTypes[i];
|
||||
|
||||
if (currentArgumentType.equals(genericReturnType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Found generic method argument at index [%s] that matches the target return type.", i));
|
||||
}
|
||||
return args[i].getClass();
|
||||
}
|
||||
|
||||
if (currentArgumentType instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) currentArgumentType;
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
|
||||
for (int j = 0; j < actualTypeArguments.length; j++) {
|
||||
final Type typeArg = actualTypeArguments[j];
|
||||
|
||||
if (typeArg.equals(genericReturnType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Found method argument at index [%s] that is parameterized with a type that matches the target return type.",
|
||||
i));
|
||||
}
|
||||
|
||||
if (args[i] instanceof Class) {
|
||||
return (Class<?>) args[i];
|
||||
} else {
|
||||
// Consider adding logic to determine the class of the
|
||||
// J'th typeArg, if possible.
|
||||
logger.info(String.format(
|
||||
"Could not determine the target type for parameterized type [%s] for method [%s].",
|
||||
typeArg, method.toGenericString()));
|
||||
|
||||
// For now, just fall back...
|
||||
return method.getReturnType();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back...
|
||||
return method.getReturnType();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,7 +261,7 @@ public abstract class GenericTypeResolver {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
return GenericTypeResolver.resolveTypeArgument((Class<?>) returnType, genericIfc);
|
||||
return resolveTypeArgument((Class<?>) returnType, genericIfc);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,7 +319,7 @@ public abstract class GenericTypeResolver {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static Class[] doResolveTypeArguments(Class ownerClass, Type ifc, Class genericIfc) {
|
||||
if (ifc instanceof ParameterizedType) {
|
||||
ParameterizedType paramIfc = (ParameterizedType) ifc;
|
||||
|
@ -236,7 +369,6 @@ public abstract class GenericTypeResolver {
|
|||
return (arg instanceof Class ? (Class) arg : Object.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve the specified generic type against the given TypeVariable map.
|
||||
* @param genericType the generic type to resolve
|
||||
|
@ -272,9 +404,9 @@ public abstract class GenericTypeResolver {
|
|||
}
|
||||
|
||||
/**
|
||||
* Build a mapping of {@link TypeVariable#getName TypeVariable names} to concrete
|
||||
* {@link Class} for the specified {@link Class}. Searches all super types,
|
||||
* enclosing types and interfaces.
|
||||
* Build a mapping of {@link TypeVariable#getName TypeVariable names} to
|
||||
* {@link Class concrete classes} for the specified {@link Class}. Searches
|
||||
* all super types, enclosing types and interfaces.
|
||||
*/
|
||||
public static Map<TypeVariable, Type> getTypeVariableMap(Class clazz) {
|
||||
Reference<Map<TypeVariable, Type>> ref = typeVariableCache.get(clazz);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
|
@ -16,51 +16,116 @@
|
|||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import static org.springframework.core.GenericTypeResolver.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class GenericTypeResolverTests {
|
||||
|
||||
@Test
|
||||
public void testSimpleInterfaceType() {
|
||||
assertEquals(String.class, GenericTypeResolver.resolveTypeArgument(MySimpleInterfaceType.class, MyInterfaceType.class));
|
||||
public void simpleInterfaceType() {
|
||||
assertEquals(String.class, resolveTypeArgument(MySimpleInterfaceType.class, MyInterfaceType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCollectionInterfaceType() {
|
||||
assertEquals(Collection.class, GenericTypeResolver.resolveTypeArgument(MyCollectionInterfaceType.class, MyInterfaceType.class));
|
||||
public void simpleCollectionInterfaceType() {
|
||||
assertEquals(Collection.class, resolveTypeArgument(MyCollectionInterfaceType.class, MyInterfaceType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleSuperclassType() {
|
||||
assertEquals(String.class, GenericTypeResolver.resolveTypeArgument(MySimpleSuperclassType.class, MySuperclassType.class));
|
||||
public void simpleSuperclassType() {
|
||||
assertEquals(String.class, resolveTypeArgument(MySimpleSuperclassType.class, MySuperclassType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCollectionSuperclassType() {
|
||||
assertEquals(Collection.class, GenericTypeResolver.resolveTypeArgument(MyCollectionSuperclassType.class, MySuperclassType.class));
|
||||
public void simpleCollectionSuperclassType() {
|
||||
assertEquals(Collection.class, resolveTypeArgument(MyCollectionSuperclassType.class, MySuperclassType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodReturnType() {
|
||||
assertEquals(Integer.class, GenericTypeResolver.resolveReturnTypeArgument(ReflectionUtils.findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
|
||||
assertEquals(String.class, GenericTypeResolver.resolveReturnTypeArgument(ReflectionUtils.findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
|
||||
assertEquals(null, GenericTypeResolver.resolveReturnTypeArgument(ReflectionUtils.findMethod(MyTypeWithMethods.class, "raw"), MyInterfaceType.class));
|
||||
assertEquals(null, GenericTypeResolver.resolveReturnTypeArgument(ReflectionUtils.findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullIfNotResolvable() {
|
||||
public void nullIfNotResolvable() {
|
||||
GenericClass<String> obj = new GenericClass<String>();
|
||||
assertNull(GenericTypeResolver.resolveTypeArgument(obj.getClass(), GenericClass.class));
|
||||
assertNull(resolveTypeArgument(obj.getClass(), GenericClass.class));
|
||||
}
|
||||
|
||||
@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(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "raw"), MyInterfaceType.class));
|
||||
assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.2
|
||||
*/
|
||||
@Test
|
||||
public void parameterizedMethodReturnTypes() {
|
||||
|
||||
Method notParameterized = findMethod(MyTypeWithMethods.class, "notParameterized", new Class[] {});
|
||||
assertEquals(String.class, resolveParameterizedReturnType(notParameterized, new Object[] {}));
|
||||
|
||||
Method notParameterizedWithArguments = findMethod(MyTypeWithMethods.class, "notParameterizedWithArguments",
|
||||
new Class[] { Integer.class, Boolean.class });
|
||||
assertEquals(String.class,
|
||||
resolveParameterizedReturnType(notParameterizedWithArguments, new Object[] { 99, true }));
|
||||
|
||||
Method createProxy = findMethod(MyTypeWithMethods.class, "createProxy", new Class[] { Object.class });
|
||||
assertEquals(String.class, resolveParameterizedReturnType(createProxy, new Object[] { "foo" }));
|
||||
|
||||
Method createNamedProxyWithDifferentTypes = findMethod(MyTypeWithMethods.class, "createNamedProxy",
|
||||
new Class[] { String.class, Object.class });
|
||||
// one argument to few
|
||||
assertNull(resolveParameterizedReturnType(createNamedProxyWithDifferentTypes, new Object[] { "enigma" }));
|
||||
assertEquals(Long.class,
|
||||
resolveParameterizedReturnType(createNamedProxyWithDifferentTypes, new Object[] { "enigma", 99L }));
|
||||
|
||||
Method createNamedProxyWithDuplicateTypes = findMethod(MyTypeWithMethods.class, "createNamedProxy",
|
||||
new Class[] { String.class, Object.class });
|
||||
assertEquals(String.class,
|
||||
resolveParameterizedReturnType(createNamedProxyWithDuplicateTypes, new Object[] { "enigma", "foo" }));
|
||||
|
||||
Method createMock = findMethod(MyTypeWithMethods.class, "createMock", new Class[] { Class.class });
|
||||
assertEquals(Runnable.class, resolveParameterizedReturnType(createMock, new Object[] { Runnable.class }));
|
||||
|
||||
Method createNamedMock = findMethod(MyTypeWithMethods.class, "createNamedMock", new Class[] { String.class,
|
||||
Class.class });
|
||||
assertEquals(Runnable.class,
|
||||
resolveParameterizedReturnType(createNamedMock, new Object[] { "foo", Runnable.class }));
|
||||
|
||||
Method createVMock = findMethod(MyTypeWithMethods.class, "createVMock",
|
||||
new Class[] { Object.class, Class.class });
|
||||
assertEquals(Runnable.class,
|
||||
resolveParameterizedReturnType(createVMock, new Object[] { "foo", Runnable.class }));
|
||||
|
||||
// Ideally we would expect String.class instead of Object.class, but
|
||||
// resolveParameterizedReturnType() does not currently support this form of
|
||||
// look-up.
|
||||
Method extractValueFrom = findMethod(MyTypeWithMethods.class, "extractValueFrom",
|
||||
new Class[] { MyInterfaceType.class });
|
||||
assertEquals(Object.class,
|
||||
resolveParameterizedReturnType(extractValueFrom, new Object[] { new MySimpleInterfaceType() }));
|
||||
|
||||
// Ideally we would expect Boolean.class instead of Object.class, but this
|
||||
// information is not available at run-time due to type erasure.
|
||||
Map<Integer, Boolean> map = new HashMap<Integer, Boolean>();
|
||||
map.put(0, false);
|
||||
map.put(1, true);
|
||||
Method extractMagicValue = findMethod(MyTypeWithMethods.class, "extractMagicValue", new Class[] { Map.class });
|
||||
assertEquals(Object.class, resolveParameterizedReturnType(extractMagicValue, new Object[] { map }));
|
||||
}
|
||||
|
||||
|
||||
|
@ -73,7 +138,6 @@ public class GenericTypeResolverTests {
|
|||
public class MyCollectionInterfaceType implements MyInterfaceType<Collection<String>> {
|
||||
}
|
||||
|
||||
|
||||
public abstract class MySuperclassType<T> {
|
||||
}
|
||||
|
||||
|
@ -83,12 +147,70 @@ public class GenericTypeResolverTests {
|
|||
public class MyCollectionSuperclassType extends MySuperclassType<Collection<String>> {
|
||||
}
|
||||
|
||||
public class MyTypeWithMethods {
|
||||
public static class MyTypeWithMethods {
|
||||
public MyInterfaceType<Integer> 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; }
|
||||
|
||||
/**
|
||||
* Simulates a factory method that wraps the supplied object in a proxy
|
||||
* of the same type.
|
||||
*/
|
||||
public static <T> 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!
|
||||
*/
|
||||
public static <T> T createNamedProxy(String name, T object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates factory methods found in libraries such as Mockito and EasyMock.
|
||||
*/
|
||||
public static <MOCK> MOCK createMock(Class<MOCK> toMock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #createMock(Class)} but adds an additional method
|
||||
* argument before the parameterized argument.
|
||||
*/
|
||||
public static <T> T createNamedMock(String name, Class<T> toMock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #createNamedMock(String, Class)} but adds an additional
|
||||
* parameterized type.
|
||||
*/
|
||||
public static <V extends Object, T> T createVMock(V name, Class<T> toMock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract some value of the type supported by the interface (i.e., by
|
||||
* a concrete, non-generic implementation of the interface).
|
||||
*/
|
||||
public static <T> T extractValueFrom(MyInterfaceType<T> myInterfaceType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract some magic value from the supplied map.
|
||||
*/
|
||||
public static <K, V> V extractMagicValue(Map<K, V> map) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class GenericClass<T> {
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.core.GenericTypeResolver">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<!-- Root Logger -->
|
||||
<root>
|
||||
<priority value="warn" />
|
||||
|
|
Loading…
Reference in New Issue