Autowiring of generic types

Includes revisions of MethodParameter and DependencyDescriptor (in particular towards a reference to the containing class). Also refines several ResolvableType method signatures.

Issue: SPR-9965
This commit is contained in:
Juergen Hoeller
2013-10-15 00:11:23 +02:00
parent 74794190a5
commit 3bd6dfe047
19 changed files with 874 additions and 351 deletions

View File

@@ -95,7 +95,7 @@ public abstract class GenericCollectionTypeResolver {
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
* expressing the type index for traversal at that level
* @return the generic type, or {@code null} if none
* @deprecated as of 4.0 in favor of using {@link ResolvableType} for arbitrary nesting levels
* @deprecated as of 4.0, in favor of using {@link ResolvableType} for arbitrary nesting levels
*/
@Deprecated
public static Class<?> getCollectionFieldType(Field collectionField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
@@ -132,7 +132,7 @@ public abstract class GenericCollectionTypeResolver {
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
* expressing the type index for traversal at that level
* @return the generic type, or {@code null} if none
* @deprecated as of 4.0 in favor of using {@link ResolvableType} for arbitrary nesting levels
* @deprecated as of 4.0, in favor of using {@link ResolvableType} for arbitrary nesting levels
*/
@Deprecated
public static Class<?> getMapKeyFieldType(Field mapField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
@@ -169,7 +169,7 @@ public abstract class GenericCollectionTypeResolver {
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
* expressing the type index for traversal at that level
* @return the generic type, or {@code null} if none
* @deprecated as of 4.0 in favor of using {@link ResolvableType} for arbitrary nesting levels
* @deprecated as of 4.0, in favor of using {@link ResolvableType} for arbitrary nesting levels
*/
@Deprecated
public static Class<?> getMapValueFieldType(Field mapField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
@@ -182,7 +182,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getCollectionParameterType(MethodParameter methodParam) {
return forMethodParameter(methodParam).asCollection().resolveGeneric();
return ResolvableType.forMethodParameter(methodParam).asCollection().resolveGeneric();
}
/**
@@ -191,7 +191,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getMapKeyParameterType(MethodParameter methodParam) {
return forMethodParameter(methodParam).asMap().resolveGeneric(0);
return ResolvableType.forMethodParameter(methodParam).asMap().resolveGeneric(0);
}
/**
@@ -200,7 +200,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getMapValueParameterType(MethodParameter methodParam) {
return forMethodParameter(methodParam).asMap().resolveGeneric(1);
return ResolvableType.forMethodParameter(methodParam).asMap().resolveGeneric(1);
}
/**
@@ -209,7 +209,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getCollectionReturnType(Method method) {
return ResolvableType.forMethodReturn(method).asCollection().resolveGeneric();
return ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric();
}
/**
@@ -223,7 +223,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getCollectionReturnType(Method method, int nestingLevel) {
return ResolvableType.forMethodReturn(method).getNested(nestingLevel).asCollection().resolveGeneric();
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asCollection().resolveGeneric();
}
/**
@@ -232,7 +232,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getMapKeyReturnType(Method method) {
return ResolvableType.forMethodReturn(method).asMap().resolveGeneric(0);
return ResolvableType.forMethodReturnType(method).asMap().resolveGeneric(0);
}
/**
@@ -244,7 +244,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getMapKeyReturnType(Method method, int nestingLevel) {
return ResolvableType.forMethodReturn(method).getNested(nestingLevel).asMap().resolveGeneric(0);
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asMap().resolveGeneric(0);
}
/**
@@ -253,7 +253,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getMapValueReturnType(Method method) {
return ResolvableType.forMethodReturn(method).asMap().resolveGeneric(1);
return ResolvableType.forMethodReturnType(method).asMap().resolveGeneric(1);
}
/**
@@ -265,14 +265,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type, or {@code null} if none
*/
public static Class<?> getMapValueReturnType(Method method, int nestingLevel) {
return ResolvableType.forMethodReturn(method).getNested(nestingLevel).asMap().resolveGeneric(1);
}
private static ResolvableType forMethodParameter(MethodParameter methodParam) {
if (methodParam.resolveClass != null) {
return ResolvableType.forMethodParameter(methodParam, methodParam.resolveClass);
}
return ResolvableType.forMethodParameter(methodParam);
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asMap().resolveGeneric(1);
}
}

View File

@@ -36,7 +36,6 @@ import org.springframework.util.ConcurrentReferenceHashMap;
*
* @author Juergen Hoeller
* @author Rob Harrop
* @author Sam Brannen
* @author Phillip Webb
* @since 2.5.2
* @see GenericCollectionTypeResolver
@@ -52,7 +51,7 @@ public abstract class GenericTypeResolver {
* Determine the target type for the given parameter specification.
* @param methodParam the method parameter specification
* @return the corresponding generic parameter type
* @deprecated as of Spring 4.0 use {@link MethodParameter#getGenericParameterType()}
* @deprecated as of Spring 4.0, use {@link MethodParameter#getGenericParameterType()}
*/
@Deprecated
public static Type getTargetType(MethodParameter methodParam) {
@@ -69,8 +68,8 @@ public abstract class GenericTypeResolver {
public static Class<?> resolveParameterType(MethodParameter methodParam, Class<?> clazz) {
Assert.notNull(methodParam, "MethodParameter must not be null");
Assert.notNull(clazz, "Class must not be null");
methodParam.resolveClass = clazz;
methodParam.setParameterType(ResolvableType.forMethodParameter(methodParam, clazz).resolve());
methodParam.setContainingClass(clazz);
methodParam.setParameterType(ResolvableType.forMethodParameter(methodParam).resolve());
return methodParam.getParameterType();
}
@@ -85,7 +84,7 @@ public abstract class GenericTypeResolver {
public static Class<?> resolveReturnType(Method method, Class<?> clazz) {
Assert.notNull(method, "Method must not be null");
Assert.notNull(clazz, "Class must not be null");
return ResolvableType.forMethodReturn(method, clazz).resolve(method.getReturnType());
return ResolvableType.forMethodReturnType(method, clazz).resolve(method.getReturnType());
}
/**
@@ -113,12 +112,14 @@ public abstract class GenericTypeResolver {
* </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}
* invoked (never {@code null})
* @param classLoader the ClassLoader to resolve class names against, if necessary
* (may be {@code null})
* @return the resolved target return type, the standard return type, or {@code null}
* @since 3.2
* @since 3.2.5
* @see #resolveReturnType
*/
public static Class<?> resolveReturnTypeForGenericMethod(Method method, Object[] args) {
public static Class<?> resolveReturnTypeForGenericMethod(Method method, Object[] args, ClassLoader classLoader) {
Assert.notNull(method, "Method must not be null");
Assert.notNull(args, "Argument array must not be null");
@@ -158,8 +159,18 @@ public abstract class GenericTypeResolver {
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type typeArg : actualTypeArguments) {
if (typeArg.equals(genericReturnType)) {
if (args[i] instanceof Class) {
return (Class<?>) args[i];
Object arg = args[i];
if (arg instanceof Class) {
return (Class<?>) arg;
}
else if (arg instanceof String) {
try {
return classLoader.loadClass((String) arg);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Could not resolve specific class name argument [" + arg + "]", ex);
}
}
else {
// Consider adding logic to determine the class of the typeArg, if possible.
@@ -187,7 +198,7 @@ public abstract class GenericTypeResolver {
*/
public static Class<?> resolveReturnTypeArgument(Method method, Class<?> genericIfc) {
Assert.notNull(method, "method must not be null");
ResolvableType resolvableType = ResolvableType.forMethodReturn(method).as(genericIfc);
ResolvableType resolvableType = ResolvableType.forMethodReturnType(method).as(genericIfc);
if (!resolvableType.hasGenerics() || resolvableType.getType() instanceof WildcardType) {
return null;
}
@@ -230,7 +241,7 @@ public abstract class GenericTypeResolver {
*/
public static Class[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc);
if(!type.hasGenerics()) {
if (!type.hasGenerics()) {
return null;
}
return type.resolveGenerics();
@@ -261,16 +272,15 @@ public abstract class GenericTypeResolver {
Map<TypeVariable, Type> typeVariableMap = typeVariableCache.get(clazz);
if (typeVariableMap == null) {
typeVariableMap = new HashMap<TypeVariable, Type>();
buildTypeVaraibleMap(ResolvableType.forClass(clazz), typeVariableMap);
buildTypeVariableMap(ResolvableType.forClass(clazz), typeVariableMap);
typeVariableCache.put(clazz, Collections.unmodifiableMap(typeVariableMap));
}
return typeVariableMap;
}
private static void buildTypeVaraibleMap(ResolvableType type,
Map<TypeVariable, Type> typeVariableMap) {
if(type != ResolvableType.NONE) {
if(type.getType() instanceof ParameterizedType) {
private static void buildTypeVariableMap(ResolvableType type, Map<TypeVariable, Type> typeVariableMap) {
if (type != ResolvableType.NONE) {
if (type.getType() instanceof ParameterizedType) {
TypeVariable<?>[] variables = type.resolve().getTypeParameters();
for (int i = 0; i < variables.length; i++) {
ResolvableType generic = type.getGeneric(i);
@@ -282,94 +292,50 @@ public abstract class GenericTypeResolver {
}
}
}
buildTypeVaraibleMap(type.getSuperType(), typeVariableMap);
buildTypeVariableMap(type.getSuperType(), typeVariableMap);
for (ResolvableType interfaceType : type.getInterfaces()) {
buildTypeVaraibleMap(interfaceType, typeVariableMap);
buildTypeVariableMap(interfaceType, typeVariableMap);
}
if (type.resolve().isMemberClass()) {
buildTypeVaraibleMap(ResolvableType.forClass(type.resolve().getEnclosingClass()),
typeVariableMap);
buildTypeVariableMap(ResolvableType.forClass(type.resolve().getEnclosingClass()), typeVariableMap);
}
}
}
/**
* Determine the raw type for the given generic parameter type.
* @param genericType the generic type to resolve
* @param typeVariableMap the TypeVariable Map to resolved against
* @return the resolved raw type
*/
@Deprecated
static Type getRawType(Type genericType, Map<TypeVariable, Type> typeVariableMap) {
Type resolvedType = genericType;
if (genericType instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) genericType;
resolvedType = typeVariableMap.get(tv);
if (resolvedType == null) {
resolvedType = extractBoundForTypeVariable(tv);
}
}
if (resolvedType instanceof ParameterizedType) {
return ((ParameterizedType) resolvedType).getRawType();
}
else {
return resolvedType;
}
}
/**
* Extracts the bound {@code Type} for a given {@link TypeVariable}.
*/
@Deprecated
static Type extractBoundForTypeVariable(TypeVariable typeVariable) {
Type[] bounds = typeVariable.getBounds();
if (bounds.length == 0) {
return Object.class;
}
Type bound = bounds[0];
if (bound instanceof TypeVariable) {
bound = extractBoundForTypeVariable((TypeVariable) bound);
}
return bound;
}
/**
* Adapts a {@code typeVariableMap} to a {@link TypeVariableResolver}.
*/
private static class TypeVariableMapResolver implements TypeVariableResolver {
private Map<TypeVariable, Type> typeVariableMap;
private final Map<TypeVariable, Type> typeVariableMap;
public TypeVariableMapResolver(Map<TypeVariable, Type> typeVariableMap) {
Assert.notNull("TypeVariableMap must not be null");
this.typeVariableMap = typeVariableMap;
}
@Override
public Type resolveVariable(TypeVariable typeVariable) {
return typeVariableMap.get(typeVariable);
}
@Override
public int hashCode() {
return typeVariableMap.hashCode();
return this.typeVariableMap.get(typeVariable);
}
@Override
public boolean equals(Object obj) {
if(obj == this) {
if (this == obj) {
return true;
}
if(obj instanceof TypeVariableMapResolver) {
if (obj instanceof TypeVariableMapResolver) {
TypeVariableMapResolver other = (TypeVariableMapResolver) obj;
return this.typeVariableMap.equals(other.typeVariableMap);
}
return false;
}
@Override
public int hashCode() {
return this.typeVariableMap.hashCode();
}
}
}

View File

@@ -23,7 +23,6 @@ import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
@@ -48,6 +47,8 @@ public class MethodParameter {
private final int parameterIndex;
private Class<?> containingClass;
private Class<?> parameterType;
private Type genericParameterType;
@@ -63,8 +64,6 @@ public class MethodParameter {
/** Map from Integer level to Integer type index */
Map<Integer, Integer> typeIndexesPerLevel;
Class<?> resolveClass;
private int hash = 0;
@@ -130,6 +129,7 @@ public class MethodParameter {
this.method = original.method;
this.constructor = original.constructor;
this.parameterIndex = original.parameterIndex;
this.containingClass = original.containingClass;
this.parameterType = original.parameterType;
this.genericParameterType = original.genericParameterType;
this.parameterAnnotations = original.parameterAnnotations;
@@ -137,7 +137,6 @@ public class MethodParameter {
this.parameterName = original.parameterName;
this.nestingLevel = original.nestingLevel;
this.typeIndexesPerLevel = original.typeIndexesPerLevel;
this.resolveClass = original.resolveClass;
this.hash = original.hash;
}
@@ -195,7 +194,7 @@ public class MethodParameter {
/**
* Return the class that declares the underlying Method or Constructor.
*/
public Class getDeclaringClass() {
public Class<?> getDeclaringClass() {
return getMember().getDeclaringClass();
}
@@ -207,6 +206,17 @@ public class MethodParameter {
return this.parameterIndex;
}
/**
* Set a containing class to resolve the parameter type against.
*/
void setContainingClass(Class<?> containingClass) {
this.containingClass = containingClass;
}
public Class<?> getContainingClass() {
return (this.containingClass != null ? this.containingClass : getDeclaringClass());
}
/**
* Set a resolved (generic) parameter type.
*/

View File

@@ -41,7 +41,7 @@ import org.springframework.util.StringUtils;
*
* <p>{@code ResolvableTypes} may be obtained from {@link #forField(Field) fields},
* {@link #forMethodParameter(Method, int) method parameters},
* {@link #forMethodReturn(Method) method returns}, {@link #forClass(Class) classes}, or
* {@link #forMethodReturnType(Method) method returns}, {@link #forClass(Class) classes}, or
* directly from a {@link #forType(Type) java.lang.reflect.Type}. Most methods on this class
* will themselves return {@link ResolvableType}s, allowing easy navigation. For example:
* <pre class="code">
@@ -59,11 +59,12 @@ import org.springframework.util.StringUtils;
* </pre>
*
* @author Phillip Webb
* @author Juergen Hoeller
* @since 4.0
* @see TypeVariableResolver
* @see #forField(Field)
* @see #forMethodParameter(Method, int)
* @see #forMethodReturn(Method)
* @see #forMethodReturnType(Method)
* @see #forConstructorParameter(Constructor, int)
* @see #forClass(Class)
* @see #forType(Type)
@@ -85,7 +86,7 @@ public final class ResolvableType implements TypeVariableResolver {
/**
* The underlying java type being managed (only ever {@code null} for {@link #NONE})
* The underlying java type being managed (only ever {@code null} for {@link #NONE}).
*/
private final Type type;
@@ -127,8 +128,7 @@ public final class ResolvableType implements TypeVariableResolver {
* {@link Class#isAssignableFrom(Class) assignable from} the given {@code type} as
* well as if all {@link #getGenerics() generics} are assignable.
* @param type the type to be checked
* @return {@code true} if the specified {@code type} can be assigned to this
* {@code type}.
* @return {@code true} if the specified {@code type} can be assigned to this {@code type}
*/
public boolean isAssignableFrom(ResolvableType type) {
return isAssignableFrom(type, false);
@@ -323,15 +323,12 @@ public final class ResolvableType implements TypeVariableResolver {
* of 1 indicates this type; 2 indicates the first nested generic; 3 the second; and so
* on. For example, given {@code List<Set<Integer>>} level 1 refers to the
* {@code List}, level 2 the {@code Set}, and level 3 the {@code Integer}.
*
* <p>The {@code typeIndexesPerLevel} map can be used to reference a specific generic
* for the given level. For example, an index of 0 would refer to a {@code Map} key;
* whereas, 1 would refer to the value. If the map does not contain a value for a
* specific level the last generic will be used (e.g. a {@code Map} value).
*
* <p>Nesting levels may also apply to array types; for example given
* {@code String[]}, a nesting level of 2 refers to {@code String}.
*
* <p>If a type does not {@link #hasGenerics() contain} generics the
* {@link #getSuperType() supertype} hierarchy will be considered.
* @param nestingLevel the required nesting level, indexed from 1 for the current
@@ -340,8 +337,7 @@ public final class ResolvableType implements TypeVariableResolver {
* level (may be {@code null})
* @return a {@link ResolvableType} for the nested level or {@link #NONE}
*/
public ResolvableType getNested(int nestingLevel,
Map<Integer, Integer> typeIndexesPerLevel) {
public ResolvableType getNested(int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
ResolvableType result = this;
for (int i = 2; i <= nestingLevel; i++) {
if (result.isArray()) {
@@ -369,7 +365,6 @@ public final class ResolvableType implements TypeVariableResolver {
* for example {@code getGeneric(1, 0)} will access the {@code String} from the nested
* {@code List}. For convenience, if no indexes are specified the first generic is
* returned.
*
* <p>If no generic is available at the specified indexes {@link #NONE} is returned.
* @param indexes the indexes that refer to the generic parameter (may be omitted to
* return the first generic)
@@ -533,10 +528,8 @@ public final class ResolvableType implements TypeVariableResolver {
public Type resolveVariable(TypeVariable<?> variable) {
Assert.notNull("Variable must not be null");
if (this.type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) this.type;
Type owner = parameterizedType.getOwnerType();
if (parameterizedType.getRawType().equals(variable.getGenericDeclaration())) {
TypeVariable<?>[] variables = resolve().getTypeParameters();
for (int i = 0; i < variables.length; i++) {
@@ -545,21 +538,18 @@ public final class ResolvableType implements TypeVariableResolver {
}
}
}
Type resolved = null;
if (this.variableResolver != null) {
resolved = this.variableResolver.resolveVariable(variable);
resolved = this.variableResolver.resolveVariable(variable);
}
if (resolved == null && owner != null) {
resolved = forType(owner, this.variableResolver).resolveVariable(variable);
}
return resolved;
}
if (this.type instanceof TypeVariable<?>) {
if (this.type instanceof TypeVariable) {
return resolveType().resolveVariable(variable);
}
return null;
}
@@ -576,19 +566,13 @@ public final class ResolvableType implements TypeVariableResolver {
StringBuilder result = new StringBuilder();
result.append(resolve() == null ? "?" : resolve().getName());
if (hasGenerics()) {
result.append("<");
result.append('<');
result.append(StringUtils.arrayToDelimitedString(getGenerics(), ", "));
result.append(">");
result.append('>');
}
return result.toString();
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.type) * 31
+ ObjectUtils.nullSafeHashCode(this.variableResolver);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
@@ -596,13 +580,17 @@ public final class ResolvableType implements TypeVariableResolver {
}
if (obj instanceof ResolvableType) {
ResolvableType other = (ResolvableType) obj;
return ObjectUtils.nullSafeEquals(this.type, other.type)
&& ObjectUtils.nullSafeEquals(this.variableResolver,
other.variableResolver);
return ObjectUtils.nullSafeEquals(this.type, other.type) &&
ObjectUtils.nullSafeEquals(this.variableResolver, other.variableResolver);
}
return false;
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.type);
}
/**
* Return a {@link ResolvableType} for the specified {@link Class}. For example
@@ -628,8 +616,7 @@ public final class ResolvableType implements TypeVariableResolver {
*/
public static ResolvableType forClass(Class<?> sourceClass, Class<?> implementationClass) {
Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(implementationClass, "ImplementationClass must not be null");
ResolvableType asType = forType(implementationClass).as(sourceClass);
ResolvableType asType = (implementationClass != null ? forType(implementationClass).as(sourceClass) : NONE);
return (asType == NONE ? forType(sourceClass) : asType);
}
@@ -646,8 +633,9 @@ public final class ResolvableType implements TypeVariableResolver {
/**
* Return a {@link ResolvableType} for the specified {@link Field} with a given
* implementation. Use this variant when the class that declares the field includes
* generic parameter variables that are satisfied by the implementation class.
* implementation.
* <p>Use this variant when the class that declares the field includes generic
* parameter variables that are satisfied by the implementation class.
* @param field the source field
* @param implementationClass the implementation class (must not be {@code null})
* @return a {@link ResolvableType} for the specified field
@@ -655,12 +643,43 @@ public final class ResolvableType implements TypeVariableResolver {
*/
public static ResolvableType forField(Field field, Class<?> implementationClass) {
Assert.notNull(field, "Field must not be null");
Assert.notNull(implementationClass, "ImplementationClass must not be null");
TypeVariableResolver variableResolver = forType(implementationClass).as(
field.getDeclaringClass());
TypeVariableResolver variableResolver = (implementationClass != null ?
forType(implementationClass).as(field.getDeclaringClass()) : null);
return forType(field.getGenericType(), variableResolver);
}
/**
* Return a {@link ResolvableType} for the specified {@link Field} with the
* given nesting level.
* @param field the source field
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
* generic type; etc)
* @see #forField(Field)
*/
public static ResolvableType forField(Field field, int nestingLevel) {
Assert.notNull(field, "Field must not be null");
return forType(field.getGenericType()).getNested(nestingLevel);
}
/**
* Return a {@link ResolvableType} for the specified {@link Field} with a given
* implementation and the given nesting level.
* <p>Use this variant when the class that declares the field includes generic
* parameter variables that are satisfied by the implementation class.
* @param field the source field
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
* generic type; etc)
* @param implementationClass the implementation class (must not be {@code null})
* @return a {@link ResolvableType} for the specified field
* @see #forField(Field)
*/
public static ResolvableType forField(Field field, int nestingLevel, Class<?> implementationClass) {
Assert.notNull(field, "Field must not be null");
TypeVariableResolver variableResolver = (implementationClass != null ?
forType(implementationClass).as(field.getDeclaringClass()) : null);
return forType(field.getGenericType(), variableResolver).getNested(nestingLevel);
}
/**
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter.
* @param constructor the source constructor (must not be {@code null})
@@ -668,11 +687,9 @@ public final class ResolvableType implements TypeVariableResolver {
* @return a {@link ResolvableType} for the specified constructor parameter
* @see #forConstructorParameter(Constructor, int, Class)
*/
public static ResolvableType forConstructorParameter(Constructor<?> constructor,
int parameterIndex) {
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) {
Assert.notNull(constructor, "Constructor must not be null");
return forMethodParameter(MethodParameter.forMethodOrConstructor(constructor,
parameterIndex));
return forMethodParameter(new MethodParameter(constructor, parameterIndex));
}
/**
@@ -686,13 +703,13 @@ public final class ResolvableType implements TypeVariableResolver {
* @return a {@link ResolvableType} for the specified constructor parameter
* @see #forConstructorParameter(Constructor, int)
*/
public static ResolvableType forConstructorParameter(Constructor<?> constructor,
int parameterIndex, Class<?> implementationClass) {
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
Class<?> implementationClass) {
Assert.notNull(constructor, "Constructor must not be null");
Assert.notNull(implementationClass, "ImplementationClass must not be null");
return forMethodParameter(
MethodParameter.forMethodOrConstructor(constructor, parameterIndex),
implementationClass);
MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex);
methodParameter.setContainingClass(implementationClass);
return forMethodParameter(methodParameter);
}
/**
@@ -705,15 +722,13 @@ public final class ResolvableType implements TypeVariableResolver {
*/
public static ResolvableType forMethodParameter(Method method, int parameterIndex) {
Assert.notNull(method, "Method must not be null");
return forMethodParameter(MethodParameter.forMethodOrConstructor(method,
parameterIndex));
return forMethodParameter(new MethodParameter(method, parameterIndex));
}
/**
* Return a {@link ResolvableType} for the specified {@link Method} parameter with a
* given implementation. Use this variant when the class that declares the method
* includes generic parameter variables that are satisfied by the implementation
* class.
* includes generic parameter variables that are satisfied by the implementation class.
* @param method the source method (must not be {@code null})
* @param parameterIndex the parameter index
* @param implementationClass the implementation class (must not be {@code null})
@@ -721,74 +736,51 @@ public final class ResolvableType implements TypeVariableResolver {
* @see #forMethodParameter(Method, int, Class)
* @see #forMethodParameter(MethodParameter)
*/
public static ResolvableType forMethodParameter(Method method, int parameterIndex,
Class<?> implementationClass) {
public static ResolvableType forMethodParameter(Method method, int parameterIndex, Class<?> implementationClass) {
Assert.notNull(method, "Method must not be null");
return forMethodParameter(
MethodParameter.forMethodOrConstructor(method, parameterIndex),
implementationClass);
MethodParameter methodParameter = new MethodParameter(method, parameterIndex);
methodParameter.setContainingClass(implementationClass);
return forMethodParameter(methodParameter);
}
/**
* Return a {@link ResolvableType} for the specified {@link MethodParameter}.
* @param methodParameter the source method parameter (must not be {@code null})
* @return a {@link ResolvableType} for the specified method parameter
* @see #forMethodParameter(MethodParameter, Class)
* @see #forMethodParameter(Method, int)
*/
public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
Assert.notNull(methodParameter, "MethodParameter must not be null");
return forType(methodParameter.getGenericParameterType()).getNested(
methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
}
/**
* Return a {@link ResolvableType} for the specified {@link MethodParameter} with a
* given implementation. Use this variant when the class that declares the method
* includes generic parameter variables that are satisfied by the implementation
* class.
* @param methodParameter the source method parameter (must not be {@code null})
* @param implementationClass the implementation class (must not be {@code null})
* @return a {@link ResolvableType} for the specified method parameter
* @see #forMethodParameter(MethodParameter)
* @see #forMethodParameter(Method, int)
*/
public static ResolvableType forMethodParameter(MethodParameter methodParameter,
Class<?> implementationClass) {
Assert.notNull(methodParameter, "MethodParameter must not be null");
Assert.notNull(implementationClass, "ImplementationClass must not be null");
TypeVariableResolver variableResolver = forType(implementationClass).as(
methodParameter.getMember().getDeclaringClass());
TypeVariableResolver variableResolver = (methodParameter.getContainingClass() != null ?
forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass()) : null);
return forType(methodParameter.getGenericParameterType(), variableResolver).getNested(
methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
}
/**
* Return a {@link ResolvableType} for the specified {@link Method} return.
* @param method the source for the method return
* Return a {@link ResolvableType} for the specified {@link Method} return type.
* @param method the source for the method return type
* @return a {@link ResolvableType} for the specified method return
* @see #forMethodReturn(Method, Class)
* @see #forMethodReturnType(Method, Class)
*/
public static ResolvableType forMethodReturn(Method method) {
public static ResolvableType forMethodReturnType(Method method) {
Assert.notNull(method, "Method must not be null");
return forType(method.getGenericReturnType());
}
/**
* Return a {@link ResolvableType} for the specified {@link Method} return. Use this
* variant when the class that declares the method includes generic parameter
* variables that are satisfied by the implementation class.
* @param method the source for the method return
* Return a {@link ResolvableType} for the specified {@link Method} return type.
* Use this variant when the class that declares the method includes generic
* parameter variables that are satisfied by the implementation class.
* @param method the source for the method return type
* @param implementationClass the implementation class (must not be {@code null})
* @return a {@link ResolvableType} for the specified method return
* @see #forMethodReturn(Method)
* @see #forMethodReturnType(Method)
*/
public static ResolvableType forMethodReturn(Method method,
Class<?> implementationClass) {
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
Assert.notNull(method, "Method must not be null");
Assert.notNull(implementationClass, "ImplementationClass must not be null");
TypeVariableResolver variableResolver = forType(implementationClass).as(
method.getDeclaringClass());
TypeVariableResolver variableResolver = (implementationClass != null ?
forType(implementationClass).as(method.getDeclaringClass()) : null);
return forType(method.getGenericReturnType(), variableResolver);
}
@@ -826,12 +818,10 @@ public final class ResolvableType implements TypeVariableResolver {
*/
private static class WildcardBounds {
private final Kind kind;
private final ResolvableType[] bounds;
/**
* Private constructor to create a new {@link WildcardBounds} instance.
* @param kind the kind of bounds
@@ -843,7 +833,6 @@ public final class ResolvableType implements TypeVariableResolver {
this.bounds = bounds;
}
/**
* Return {@code true} if this bounds is the same kind as the specified bounds.
*/
@@ -868,18 +857,16 @@ public final class ResolvableType implements TypeVariableResolver {
}
private boolean isAssignable(ResolvableType source, ResolvableType from) {
return (this.kind == Kind.UPPER ? source.isAssignableFrom(from)
: from.isAssignableFrom(source));
return (this.kind == Kind.UPPER ? source.isAssignableFrom(from) : from.isAssignableFrom(source));
}
/**
* Return the underlying bounds.
*/
public ResolvableType[] getBounds() {
return bounds;
return this.bounds;
}
/**
* Get a {@link WildcardBounds} instance for the specified type, returning
* {@code null} if the specified type cannot be resolved to a {@link WildcardType}.
@@ -888,17 +875,15 @@ public final class ResolvableType implements TypeVariableResolver {
*/
public static WildcardBounds get(ResolvableType type) {
ResolvableType resolveToWildcard = type;
while(!(resolveToWildcard.getType() instanceof WildcardType)) {
while (!(resolveToWildcard.getType() instanceof WildcardType)) {
if (resolveToWildcard == NONE) {
return null;
}
resolveToWildcard = resolveToWildcard.resolveType();
}
WildcardType wildcardType = (WildcardType) resolveToWildcard.type;
Kind boundsType = (wildcardType.getLowerBounds().length > 0 ? Kind.LOWER
: Kind.UPPER);
Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds()
: wildcardType.getLowerBounds();
Kind boundsType = (wildcardType.getLowerBounds().length > 0 ? Kind.LOWER : Kind.UPPER);
Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds();
ResolvableType[] resolvableBounds = new ResolvableType[bounds.length];
for (int i = 0; i < bounds.length; i++) {
resolvableBounds[i] = forType(bounds[i], type.variableResolver);
@@ -906,11 +891,10 @@ public final class ResolvableType implements TypeVariableResolver {
return new WildcardBounds(boundsType, resolvableBounds);
}
/**
* The various kinds of bounds.
*/
static enum Kind { UPPER, LOWER }
static enum Kind {UPPER, LOWER}
}

View File

@@ -24,14 +24,16 @@ import java.lang.reflect.TypeVariable;
*
* @author Phillip Webb
* @since 4.0
* @see ResolvableType
* @see GenericTypeResolver
*/
public interface TypeVariableResolver {
interface TypeVariableResolver {
/**
* Resolve the specified type variable.
* @param typeVariable the type variable to resolve (must not be {@code null})
* @return the resolved {@link java.lang.reflect.Type} for the variable or
* {@code null} if the variable cannot be resolved.
* {@code null} if the variable cannot be resolved.
*/
Type resolveVariable(TypeVariable<?> typeVariable);