/*
 * Copyright 2002-2019 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.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;

import org.springframework.core.SerializableTypeWrapper.FieldTypeProvider;
import org.springframework.core.SerializableTypeWrapper.MethodParameterTypeProvider;
import org.springframework.core.SerializableTypeWrapper.TypeProvider;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

Encapsulates a Java Type, providing access to supertypes, interfaces, and generic parameters along with the ability to ultimately resolve to a Class.

ResolvableTypes may be obtained from fields, method parameters, method returns or classes. Most methods on this class will themselves return ResolvableTypes, allowing easy navigation. For example:

private HashMap<Integer, List<String>> myMap;
public void example() {
    ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap"));
    t.getSuperType(); // AbstractMap<Integer, List<String>>
    t.asMap(); // Map<Integer, List<String>>
    t.getGeneric(0).resolve(); // Integer
    t.getGeneric(1).resolve(); // List
    t.getGeneric(1); // List<String>
    t.resolveGeneric(1, 0); // String
}
Author:Phillip Webb, Juergen Hoeller, Stephane Nicoll
See Also:
Since:4.0
/** * Encapsulates a Java {@link java.lang.reflect.Type}, providing access to * {@link #getSuperType() supertypes}, {@link #getInterfaces() interfaces}, and * {@link #getGeneric(int...) generic parameters} along with the ability to ultimately * {@link #resolve() resolve} to a {@link java.lang.Class}. * * <p>{@code ResolvableTypes} may be obtained from {@link #forField(Field) fields}, * {@link #forMethodParameter(Method, int) method parameters}, * {@link #forMethodReturnType(Method) method returns} or * {@link #forClass(Class) classes}. Most methods on this class will themselves return * {@link ResolvableType ResolvableTypes}, allowing easy navigation. For example: * <pre class="code"> * private HashMap&lt;Integer, List&lt;String&gt;&gt; myMap; * * public void example() { * ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap")); * t.getSuperType(); // AbstractMap&lt;Integer, List&lt;String&gt;&gt; * t.asMap(); // Map&lt;Integer, List&lt;String&gt;&gt; * t.getGeneric(0).resolve(); // Integer * t.getGeneric(1).resolve(); // List * t.getGeneric(1); // List&lt;String&gt; * t.resolveGeneric(1, 0); // String * } * </pre> * * @author Phillip Webb * @author Juergen Hoeller * @author Stephane Nicoll * @since 4.0 * @see #forField(Field) * @see #forMethodParameter(Method, int) * @see #forMethodReturnType(Method) * @see #forConstructorParameter(Constructor, int) * @see #forClass(Class) * @see #forType(Type) * @see #forInstance(Object) * @see ResolvableTypeProvider */
@SuppressWarnings("serial") public class ResolvableType implements Serializable {
ResolvableType returned when no value is available. NONE is used in preference to null so that multiple method calls can be safely chained.
/** * {@code ResolvableType} returned when no value is available. {@code NONE} is used * in preference to {@code null} so that multiple method calls can be safely chained. */
public static final ResolvableType NONE = new ResolvableType(EmptyType.INSTANCE, null, null, 0); private static final ResolvableType[] EMPTY_TYPES_ARRAY = new ResolvableType[0]; private static final ConcurrentReferenceHashMap<ResolvableType, ResolvableType> cache = new ConcurrentReferenceHashMap<>(256);
The underlying Java type being managed.
/** * The underlying Java type being managed. */
private final Type type;
Optional provider for the type.
/** * Optional provider for the type. */
@Nullable private final TypeProvider typeProvider;
The VariableResolver to use or null if no resolver is available.
/** * The {@code VariableResolver} to use or {@code null} if no resolver is available. */
@Nullable private final VariableResolver variableResolver;
The component type for an array or null if the type should be deduced.
/** * The component type for an array or {@code null} if the type should be deduced. */
@Nullable private final ResolvableType componentType; @Nullable private final Integer hash; @Nullable private Class<?> resolved; @Nullable private volatile ResolvableType superType; @Nullable private volatile ResolvableType[] interfaces; @Nullable private volatile ResolvableType[] generics;
Private constructor used to create a new ResolvableType for cache key purposes, with no upfront resolution.
/** * Private constructor used to create a new {@link ResolvableType} for cache key purposes, * with no upfront resolution. */
private ResolvableType( Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { this.type = type; this.typeProvider = typeProvider; this.variableResolver = variableResolver; this.componentType = null; this.hash = calculateHashCode(); this.resolved = null; }
Private constructor used to create a new ResolvableType for cache value purposes, with upfront resolution and a pre-calculated hash.
Since:4.2
/** * Private constructor used to create a new {@link ResolvableType} for cache value purposes, * with upfront resolution and a pre-calculated hash. * @since 4.2 */
private ResolvableType(Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver, @Nullable Integer hash) { this.type = type; this.typeProvider = typeProvider; this.variableResolver = variableResolver; this.componentType = null; this.hash = hash; this.resolved = resolveClass(); }
Private constructor used to create a new ResolvableType for uncached purposes, with upfront resolution but lazily calculated hash.
/** * Private constructor used to create a new {@link ResolvableType} for uncached purposes, * with upfront resolution but lazily calculated hash. */
private ResolvableType(Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver, @Nullable ResolvableType componentType) { this.type = type; this.typeProvider = typeProvider; this.variableResolver = variableResolver; this.componentType = componentType; this.hash = null; this.resolved = resolveClass(); }
Private constructor used to create a new ResolvableType on a Class basis. Avoids all instanceof checks in order to create a straight Class wrapper.
Since:4.2
/** * Private constructor used to create a new {@link ResolvableType} on a {@link Class} basis. * Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper. * @since 4.2 */
private ResolvableType(@Nullable Class<?> clazz) { this.resolved = (clazz != null ? clazz : Object.class); this.type = this.resolved; this.typeProvider = null; this.variableResolver = null; this.componentType = null; this.hash = null; }
Return the underling Java Type being managed.
/** * Return the underling Java {@link Type} being managed. */
public Type getType() { return SerializableTypeWrapper.unwrap(this.type); }
Return the underlying Java Class being managed, if available; otherwise null.
/** * Return the underlying Java {@link Class} being managed, if available; * otherwise {@code null}. */
@Nullable public Class<?> getRawClass() { if (this.type == this.resolved) { return this.resolved; } Type rawType = this.type; if (rawType instanceof ParameterizedType) { rawType = ((ParameterizedType) rawType).getRawType(); } return (rawType instanceof Class ? (Class<?>) rawType : null); }
Return the underlying source of the resolvable type. Will return a Field, MethodParameter or Type depending on how the ResolvableType was constructed. With the exception of the NONE constant, this method will never return null. This method is primarily to provide access to additional type information or meta-data that alternative JVM languages may provide.
/** * Return the underlying source of the resolvable type. Will return a {@link Field}, * {@link MethodParameter} or {@link Type} depending on how the {@link ResolvableType} * was constructed. With the exception of the {@link #NONE} constant, this method will * never return {@code null}. This method is primarily to provide access to additional * type information or meta-data that alternative JVM languages may provide. */
public Object getSource() { Object source = (this.typeProvider != null ? this.typeProvider.getSource() : null); return (source != null ? source : this.type); }
Return this type as a resolved Class, falling back to Object if no specific class can be resolved.
See Also:
Returns:the resolved Class or the Object fallback
Since:5.1
/** * Return this type as a resolved {@code Class}, falling back to * {@link java.lang.Object} if no specific class can be resolved. * @return the resolved {@link Class} or the {@code Object} fallback * @since 5.1 * @see #getRawClass() * @see #resolve(Class) */
public Class<?> toClass() { return resolve(Object.class); }
Determine whether the given object is an instance of this ResolvableType.
Params:
  • obj – the object to check
See Also:
Since:4.2
/** * Determine whether the given object is an instance of this {@code ResolvableType}. * @param obj the object to check * @since 4.2 * @see #isAssignableFrom(Class) */
public boolean isInstance(@Nullable Object obj) { return (obj != null && isAssignableFrom(obj.getClass())); }
Determine whether this ResolvableType is assignable from the specified other type.
Params:
  • other – the type to be checked against (as a Class)
See Also:
Since:4.2
/** * Determine whether this {@code ResolvableType} is assignable from the * specified other type. * @param other the type to be checked against (as a {@code Class}) * @since 4.2 * @see #isAssignableFrom(ResolvableType) */
public boolean isAssignableFrom(Class<?> other) { return isAssignableFrom(forClass(other), null); }
Determine whether this ResolvableType is assignable from the specified other type.

Attempts to follow the same rules as the Java compiler, considering whether both the resolved Class is assignable from the given type as well as whether all generics are assignable.

Params:
  • other – the type to be checked against (as a ResolvableType)
Returns:true if the specified other type can be assigned to this ResolvableType; false otherwise
/** * Determine whether this {@code ResolvableType} is assignable from the * specified other type. * <p>Attempts to follow the same rules as the Java compiler, considering * whether both the {@link #resolve() resolved} {@code Class} is * {@link Class#isAssignableFrom(Class) assignable from} the given type * as well as whether all {@link #getGenerics() generics} are assignable. * @param other the type to be checked against (as a {@code ResolvableType}) * @return {@code true} if the specified other type can be assigned to this * {@code ResolvableType}; {@code false} otherwise */
public boolean isAssignableFrom(ResolvableType other) { return isAssignableFrom(other, null); } private boolean isAssignableFrom(ResolvableType other, @Nullable Map<Type, Type> matchedBefore) { Assert.notNull(other, "ResolvableType must not be null"); // If we cannot resolve types, we are not assignable if (this == NONE || other == NONE) { return false; } // Deal with array by delegating to the component type if (isArray()) { return (other.isArray() && getComponentType().isAssignableFrom(other.getComponentType())); } if (matchedBefore != null && matchedBefore.get(this.type) == other.type) { return true; } // Deal with wildcard bounds WildcardBounds ourBounds = WildcardBounds.get(this); WildcardBounds typeBounds = WildcardBounds.get(other); // In the form X is assignable to <? extends Number> if (typeBounds != null) { return (ourBounds != null && ourBounds.isSameKind(typeBounds) && ourBounds.isAssignableFrom(typeBounds.getBounds())); } // In the form <? extends Number> is assignable to X... if (ourBounds != null) { return ourBounds.isAssignableFrom(other); } // Main assignability check about to follow boolean exactMatch = (matchedBefore != null); // We're checking nested generic variables now... boolean checkGenerics = true; Class<?> ourResolved = null; if (this.type instanceof TypeVariable) { TypeVariable<?> variable = (TypeVariable<?>) this.type; // Try default variable resolution if (this.variableResolver != null) { ResolvableType resolved = this.variableResolver.resolveVariable(variable); if (resolved != null) { ourResolved = resolved.resolve(); } } if (ourResolved == null) { // Try variable resolution against target type if (other.variableResolver != null) { ResolvableType resolved = other.variableResolver.resolveVariable(variable); if (resolved != null) { ourResolved = resolved.resolve(); checkGenerics = false; } } } if (ourResolved == null) { // Unresolved type variable, potentially nested -> never insist on exact match exactMatch = false; } } if (ourResolved == null) { ourResolved = resolve(Object.class); } Class<?> otherResolved = other.toClass(); // We need an exact type match for generics // List<CharSequence> is not assignable from List<String> if (exactMatch ? !ourResolved.equals(otherResolved) : !ClassUtils.isAssignable(ourResolved, otherResolved)) { return false; } if (checkGenerics) { // Recursively check each generic ResolvableType[] ourGenerics = getGenerics(); ResolvableType[] typeGenerics = other.as(ourResolved).getGenerics(); if (ourGenerics.length != typeGenerics.length) { return false; } if (matchedBefore == null) { matchedBefore = new IdentityHashMap<>(1); } matchedBefore.put(this.type, other.type); for (int i = 0; i < ourGenerics.length; i++) { if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) { return false; } } } return true; }
Return true if this type resolves to a Class that represents an array.
See Also:
/** * Return {@code true} if this type resolves to a Class that represents an array. * @see #getComponentType() */
public boolean isArray() { if (this == NONE) { return false; } return ((this.type instanceof Class && ((Class<?>) this.type).isArray()) || this.type instanceof GenericArrayType || resolveType().isArray()); }
Return the ResolvableType representing the component type of the array or NONE if this type does not represent an array.
See Also:
/** * Return the ResolvableType representing the component type of the array or * {@link #NONE} if this type does not represent an array. * @see #isArray() */
public ResolvableType getComponentType() { if (this == NONE) { return NONE; } if (this.componentType != null) { return this.componentType; } if (this.type instanceof Class) { Class<?> componentType = ((Class<?>) this.type).getComponentType(); return forType(componentType, this.variableResolver); } if (this.type instanceof GenericArrayType) { return forType(((GenericArrayType) this.type).getGenericComponentType(), this.variableResolver); } return resolveType().getComponentType(); }
Convenience method to return this type as a resolvable Collection type. Returns NONE if this type does not implement or extend Collection.
See Also:
/** * Convenience method to return this type as a resolvable {@link Collection} type. * Returns {@link #NONE} if this type does not implement or extend * {@link Collection}. * @see #as(Class) * @see #asMap() */
public ResolvableType asCollection() { return as(Collection.class); }
Convenience method to return this type as a resolvable Map type. Returns NONE if this type does not implement or extend Map.
See Also:
/** * Convenience method to return this type as a resolvable {@link Map} type. * Returns {@link #NONE} if this type does not implement or extend * {@link Map}. * @see #as(Class) * @see #asCollection() */
public ResolvableType asMap() { return as(Map.class); }
Return this type as a ResolvableType of the specified class. Searches supertype and interface hierarchies to find a match, returning NONE if this type does not implement or extend the specified class.
Params:
  • type – the required type (typically narrowed)
See Also:
Returns:a ResolvableType representing this object as the specified type, or NONE if not resolvable as that type
/** * Return this type as a {@link ResolvableType} of the specified class. Searches * {@link #getSuperType() supertype} and {@link #getInterfaces() interface} * hierarchies to find a match, returning {@link #NONE} if this type does not * implement or extend the specified class. * @param type the required type (typically narrowed) * @return a {@link ResolvableType} representing this object as the specified * type, or {@link #NONE} if not resolvable as that type * @see #asCollection() * @see #asMap() * @see #getSuperType() * @see #getInterfaces() */
public ResolvableType as(Class<?> type) { if (this == NONE) { return NONE; } Class<?> resolved = resolve(); if (resolved == null || resolved == type) { return this; } for (ResolvableType interfaceType : getInterfaces()) { ResolvableType interfaceAsType = interfaceType.as(type); if (interfaceAsType != NONE) { return interfaceAsType; } } return getSuperType().as(type); }
Return a ResolvableType representing the direct supertype of this type. If no supertype is available this method returns NONE.

Note: The resulting ResolvableType instance may not be Serializable.

See Also:
/** * Return a {@link ResolvableType} representing the direct supertype of this type. * If no supertype is available this method returns {@link #NONE}. * <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. * @see #getInterfaces() */
public ResolvableType getSuperType() { Class<?> resolved = resolve(); if (resolved == null || resolved.getGenericSuperclass() == null) { return NONE; } ResolvableType superType = this.superType; if (superType == null) { superType = forType(resolved.getGenericSuperclass(), this); this.superType = superType; } return superType; }
Return a ResolvableType array representing the direct interfaces implemented by this type. If this type does not implement any interfaces an empty array is returned.

Note: The resulting ResolvableType instances may not be Serializable.

See Also:
/** * Return a {@link ResolvableType} array representing the direct interfaces * implemented by this type. If this type does not implement any interfaces an * empty array is returned. * <p>Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}. * @see #getSuperType() */
public ResolvableType[] getInterfaces() { Class<?> resolved = resolve(); if (resolved == null) { return EMPTY_TYPES_ARRAY; } ResolvableType[] interfaces = this.interfaces; if (interfaces == null) { Type[] genericIfcs = resolved.getGenericInterfaces(); interfaces = new ResolvableType[genericIfcs.length]; for (int i = 0; i < genericIfcs.length; i++) { interfaces[i] = forType(genericIfcs[i], this); } this.interfaces = interfaces; } return interfaces; }
Return true if this type contains generic parameters.
See Also:
/** * Return {@code true} if this type contains generic parameters. * @see #getGeneric(int...) * @see #getGenerics() */
public boolean hasGenerics() { return (getGenerics().length > 0); }
Return true if this type contains unresolvable generics only, that is, no substitute for any of its declared type variables.
/** * Return {@code true} if this type contains unresolvable generics only, * that is, no substitute for any of its declared type variables. */
boolean isEntirelyUnresolvable() { if (this == NONE) { return false; } ResolvableType[] generics = getGenerics(); for (ResolvableType generic : generics) { if (!generic.isUnresolvableTypeVariable() && !generic.isWildcardWithoutBounds()) { return false; } } return true; }
Determine whether the underlying type has any unresolvable generics: either through an unresolvable type variable on the type itself or through implementing a generic interface in a raw fashion, i.e. without substituting that interface's type variables. The result will be true only in those two scenarios.
/** * Determine whether the underlying type has any unresolvable generics: * either through an unresolvable type variable on the type itself * or through implementing a generic interface in a raw fashion, * i.e. without substituting that interface's type variables. * The result will be {@code true} only in those two scenarios. */
public boolean hasUnresolvableGenerics() { if (this == NONE) { return false; } ResolvableType[] generics = getGenerics(); for (ResolvableType generic : generics) { if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds()) { return true; } } Class<?> resolved = resolve(); if (resolved != null) { for (Type genericInterface : resolved.getGenericInterfaces()) { if (genericInterface instanceof Class) { if (forClass((Class<?>) genericInterface).hasGenerics()) { return true; } } } return getSuperType().hasUnresolvableGenerics(); } return false; }
Determine whether the underlying type is a type variable that cannot be resolved through the associated variable resolver.
/** * Determine whether the underlying type is a type variable that * cannot be resolved through the associated variable resolver. */
private boolean isUnresolvableTypeVariable() { if (this.type instanceof TypeVariable) { if (this.variableResolver == null) { return true; } TypeVariable<?> variable = (TypeVariable<?>) this.type; ResolvableType resolved = this.variableResolver.resolveVariable(variable); if (resolved == null || resolved.isUnresolvableTypeVariable()) { return true; } } return false; }
Determine whether the underlying type represents a wildcard without specific bounds (i.e., equal to ? extends Object).
/** * Determine whether the underlying type represents a wildcard * without specific bounds (i.e., equal to {@code ? extends Object}). */
private boolean isWildcardWithoutBounds() { if (this.type instanceof WildcardType) { WildcardType wt = (WildcardType) this.type; if (wt.getLowerBounds().length == 0) { Type[] upperBounds = wt.getUpperBounds(); if (upperBounds.length == 0 || (upperBounds.length == 1 && Object.class == upperBounds[0])) { return true; } } } return false; }
Return a ResolvableType for the specified nesting level. See getNested(int, Map<Integer,Integer>) for details.
Params:
  • nestingLevel – the nesting level
Returns:the ResolvableType type, or #NONE
/** * Return a {@link ResolvableType} for the specified nesting level. * See {@link #getNested(int, Map)} for details. * @param nestingLevel the nesting level * @return the {@link ResolvableType} type, or {@code #NONE} */
public ResolvableType getNested(int nestingLevel) { return getNested(nestingLevel, null); }
Return a ResolvableType for the specified nesting level.

The nesting level refers to the specific generic parameter that should be returned. A nesting level of 1 indicates this type; 2 indicates the first nested generic; 3 the second; and so on. For example, given List<Set<Integer>> level 1 refers to the List, level 2 the Set, and level 3 the Integer.

The typeIndexesPerLevel map can be used to reference a specific generic for the given level. For example, an index of 0 would refer to a 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 Map value).

Nesting levels may also apply to array types; for example given String[], a nesting level of 2 refers to String.

If a type does not contain generics the supertype hierarchy will be considered.

Params:
  • nestingLevel – the required nesting level, indexed from 1 for the current type, 2 for the first nested generic, 3 for the second and so on
  • typeIndexesPerLevel – a map containing the generic index for a given nesting level (may be null)
Returns:a ResolvableType for the nested level, or NONE
/** * Return a {@link ResolvableType} for the specified nesting level. * <p>The nesting level refers to the specific generic parameter that should be returned. * A nesting level 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 type, 2 for the first nested generic, 3 for the second and so on * @param typeIndexesPerLevel a map containing the generic index for a given * nesting level (may be {@code null}) * @return a {@link ResolvableType} for the nested level, or {@link #NONE} */
public ResolvableType getNested(int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel) { ResolvableType result = this; for (int i = 2; i <= nestingLevel; i++) { if (result.isArray()) { result = result.getComponentType(); } else { // Handle derived types while (result != ResolvableType.NONE && !result.hasGenerics()) { result = result.getSuperType(); } Integer index = (typeIndexesPerLevel != null ? typeIndexesPerLevel.get(i) : null); index = (index == null ? result.getGenerics().length - 1 : index); result = result.getGeneric(index); } } return result; }
Return a ResolvableType representing the generic parameter for the given indexes. Indexes are zero based; for example given the type Map<Integer, List<String>>, getGeneric(0) will access the Integer. Nested generics can be accessed by specifying multiple indexes; for example getGeneric(1, 0) will access the String from the nested List. For convenience, if no indexes are specified the first generic is returned.

If no generic is available at the specified indexes NONE is returned.

Params:
  • indexes – the indexes that refer to the generic parameter (may be omitted to return the first generic)
See Also:
Returns:a ResolvableType for the specified generic, or NONE
/** * Return a {@link ResolvableType} representing the generic parameter for the * given indexes. Indexes are zero based; for example given the type * {@code Map<Integer, List<String>>}, {@code getGeneric(0)} will access the * {@code Integer}. Nested generics can be accessed by specifying multiple indexes; * 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) * @return a {@link ResolvableType} for the specified generic, or {@link #NONE} * @see #hasGenerics() * @see #getGenerics() * @see #resolveGeneric(int...) * @see #resolveGenerics() */
public ResolvableType getGeneric(@Nullable int... indexes) { ResolvableType[] generics = getGenerics(); if (indexes == null || indexes.length == 0) { return (generics.length == 0 ? NONE : generics[0]); } ResolvableType generic = this; for (int index : indexes) { generics = generic.getGenerics(); if (index < 0 || index >= generics.length) { return NONE; } generic = generics[index]; } return generic; }
Return an array of ResolvableTypes representing the generic parameters of this type. If no generics are available an empty array is returned. If you need to access a specific generic consider using the getGeneric(int...) method as it allows access to nested generics and protects against IndexOutOfBoundsExceptions.
See Also:
Returns:an array of ResolvableTypes representing the generic parameters (never null)
/** * Return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters of * this type. If no generics are available an empty array is returned. If you need to * access a specific generic consider using the {@link #getGeneric(int...)} method as * it allows access to nested generics and protects against * {@code IndexOutOfBoundsExceptions}. * @return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters * (never {@code null}) * @see #hasGenerics() * @see #getGeneric(int...) * @see #resolveGeneric(int...) * @see #resolveGenerics() */
public ResolvableType[] getGenerics() { if (this == NONE) { return EMPTY_TYPES_ARRAY; } ResolvableType[] generics = this.generics; if (generics == null) { if (this.type instanceof Class) { Type[] typeParams = ((Class<?>) this.type).getTypeParameters(); generics = new ResolvableType[typeParams.length]; for (int i = 0; i < generics.length; i++) { generics[i] = ResolvableType.forType(typeParams[i], this); } } else if (this.type instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments(); generics = new ResolvableType[actualTypeArguments.length]; for (int i = 0; i < actualTypeArguments.length; i++) { generics[i] = forType(actualTypeArguments[i], this.variableResolver); } } else { generics = resolveType().getGenerics(); } this.generics = generics; } return generics; }
Convenience method that will get and resolve generic parameters.
See Also:
Returns:an array of resolved generic parameters (the resulting array will never be null, but it may contain null elements})
/** * Convenience method that will {@link #getGenerics() get} and * {@link #resolve() resolve} generic parameters. * @return an array of resolved generic parameters (the resulting array * will never be {@code null}, but it may contain {@code null} elements}) * @see #getGenerics() * @see #resolve() */
public Class<?>[] resolveGenerics() { ResolvableType[] generics = getGenerics(); Class<?>[] resolvedGenerics = new Class<?>[generics.length]; for (int i = 0; i < generics.length; i++) { resolvedGenerics[i] = generics[i].resolve(); } return resolvedGenerics; }
Convenience method that will get and resolve generic parameters, using the specified fallback if any type cannot be resolved.
Params:
  • fallback – the fallback class to use if resolution fails
See Also:
Returns:an array of resolved generic parameters
/** * Convenience method that will {@link #getGenerics() get} and {@link #resolve() * resolve} generic parameters, using the specified {@code fallback} if any type * cannot be resolved. * @param fallback the fallback class to use if resolution fails * @return an array of resolved generic parameters * @see #getGenerics() * @see #resolve() */
public Class<?>[] resolveGenerics(Class<?> fallback) { ResolvableType[] generics = getGenerics(); Class<?>[] resolvedGenerics = new Class<?>[generics.length]; for (int i = 0; i < generics.length; i++) { resolvedGenerics[i] = generics[i].resolve(fallback); } return resolvedGenerics; }
Convenience method that will get and resolve a specific generic parameters.
Params:
  • indexes – the indexes that refer to the generic parameter (may be omitted to return the first generic)
See Also:
Returns:a resolved Class or null
/** * Convenience method that will {@link #getGeneric(int...) get} and * {@link #resolve() resolve} a specific generic parameters. * @param indexes the indexes that refer to the generic parameter * (may be omitted to return the first generic) * @return a resolved {@link Class} or {@code null} * @see #getGeneric(int...) * @see #resolve() */
@Nullable public Class<?> resolveGeneric(int... indexes) { return getGeneric(indexes).resolve(); }
Resolve this type to a Class, returning null if the type cannot be resolved. This method will consider bounds of TypeVariables and WildcardTypes if direct resolution fails; however, bounds of Object.class will be ignored.

If this method returns a non-null Class and hasGenerics() returns false, the given type effectively wraps a plain Class, allowing for plain Class processing if desirable.

See Also:
Returns:the resolved Class, or null if not resolvable
/** * Resolve this type to a {@link java.lang.Class}, returning {@code null} * if the type cannot be resolved. This method will consider bounds of * {@link TypeVariable TypeVariables} and {@link WildcardType WildcardTypes} if * direct resolution fails; however, bounds of {@code Object.class} will be ignored. * <p>If this method returns a non-null {@code Class} and {@link #hasGenerics()} * returns {@code false}, the given type effectively wraps a plain {@code Class}, * allowing for plain {@code Class} processing if desirable. * @return the resolved {@link Class}, or {@code null} if not resolvable * @see #resolve(Class) * @see #resolveGeneric(int...) * @see #resolveGenerics() */
@Nullable public Class<?> resolve() { return this.resolved; }
Resolve this type to a Class, returning the specified fallback if the type cannot be resolved. This method will consider bounds of TypeVariables and WildcardTypes if direct resolution fails; however, bounds of Object.class will be ignored.
Params:
  • fallback – the fallback class to use if resolution fails
See Also:
Returns:the resolved Class or the fallback
/** * Resolve this type to a {@link java.lang.Class}, returning the specified * {@code fallback} if the type cannot be resolved. This method will consider bounds * of {@link TypeVariable TypeVariables} and {@link WildcardType WildcardTypes} if * direct resolution fails; however, bounds of {@code Object.class} will be ignored. * @param fallback the fallback class to use if resolution fails * @return the resolved {@link Class} or the {@code fallback} * @see #resolve() * @see #resolveGeneric(int...) * @see #resolveGenerics() */
public Class<?> resolve(Class<?> fallback) { return (this.resolved != null ? this.resolved : fallback); } @Nullable private Class<?> resolveClass() { if (this.type == EmptyType.INSTANCE) { return null; } if (this.type instanceof Class) { return (Class<?>) this.type; } if (this.type instanceof GenericArrayType) { Class<?> resolvedComponent = getComponentType().resolve(); return (resolvedComponent != null ? Array.newInstance(resolvedComponent, 0).getClass() : null); } return resolveType().resolve(); }
Resolve this type by a single level, returning the resolved value or NONE.

Note: The returned ResolvableType should only be used as an intermediary as it cannot be serialized.

/** * Resolve this type by a single level, returning the resolved value or {@link #NONE}. * <p>Note: The returned {@link ResolvableType} should only be used as an intermediary * as it cannot be serialized. */
ResolvableType resolveType() { if (this.type instanceof ParameterizedType) { return forType(((ParameterizedType) this.type).getRawType(), this.variableResolver); } if (this.type instanceof WildcardType) { Type resolved = resolveBounds(((WildcardType) this.type).getUpperBounds()); if (resolved == null) { resolved = resolveBounds(((WildcardType) this.type).getLowerBounds()); } return forType(resolved, this.variableResolver); } if (this.type instanceof TypeVariable) { TypeVariable<?> variable = (TypeVariable<?>) this.type; // Try default variable resolution if (this.variableResolver != null) { ResolvableType resolved = this.variableResolver.resolveVariable(variable); if (resolved != null) { return resolved; } } // Fallback to bounds return forType(resolveBounds(variable.getBounds()), this.variableResolver); } return NONE; } @Nullable private Type resolveBounds(Type[] bounds) { if (bounds.length == 0 || bounds[0] == Object.class) { return null; } return bounds[0]; } @Nullable private ResolvableType resolveVariable(TypeVariable<?> variable) { if (this.type instanceof TypeVariable) { return resolveType().resolveVariable(variable); } if (this.type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) this.type; Class<?> resolved = resolve(); if (resolved == null) { return null; } TypeVariable<?>[] variables = resolved.getTypeParameters(); for (int i = 0; i < variables.length; i++) { if (ObjectUtils.nullSafeEquals(variables[i].getName(), variable.getName())) { Type actualType = parameterizedType.getActualTypeArguments()[i]; return forType(actualType, this.variableResolver); } } Type ownerType = parameterizedType.getOwnerType(); if (ownerType != null) { return forType(ownerType, this.variableResolver).resolveVariable(variable); } } if (this.variableResolver != null) { return this.variableResolver.resolveVariable(variable); } return null; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof ResolvableType)) { return false; } ResolvableType otherType = (ResolvableType) other; if (!ObjectUtils.nullSafeEquals(this.type, otherType.type)) { return false; } if (this.typeProvider != otherType.typeProvider && (this.typeProvider == null || otherType.typeProvider == null || !ObjectUtils.nullSafeEquals(this.typeProvider.getType(), otherType.typeProvider.getType()))) { return false; } if (this.variableResolver != otherType.variableResolver && (this.variableResolver == null || otherType.variableResolver == null || !ObjectUtils.nullSafeEquals(this.variableResolver.getSource(), otherType.variableResolver.getSource()))) { return false; } if (!ObjectUtils.nullSafeEquals(this.componentType, otherType.componentType)) { return false; } return true; } @Override public int hashCode() { return (this.hash != null ? this.hash : calculateHashCode()); } private int calculateHashCode() { int hashCode = ObjectUtils.nullSafeHashCode(this.type); if (this.typeProvider != null) { hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.typeProvider.getType()); } if (this.variableResolver != null) { hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.variableResolver.getSource()); } if (this.componentType != null) { hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.componentType); } return hashCode; }
Adapts this ResolvableType to a VariableResolver.
/** * Adapts this {@link ResolvableType} to a {@link VariableResolver}. */
@Nullable VariableResolver asVariableResolver() { if (this == NONE) { return null; } return new DefaultVariableResolver(); }
Custom serialization support for NONE.
/** * Custom serialization support for {@link #NONE}. */
private Object readResolve() { return (this.type == EmptyType.INSTANCE ? NONE : this); }
Return a String representation of this type in its fully resolved form (including any generic parameters).
/** * Return a String representation of this type in its fully resolved form * (including any generic parameters). */
@Override public String toString() { if (isArray()) { return getComponentType() + "[]"; } if (this.resolved == null) { return "?"; } if (this.type instanceof TypeVariable) { TypeVariable<?> variable = (TypeVariable<?>) this.type; if (this.variableResolver == null || this.variableResolver.resolveVariable(variable) == null) { // Don't bother with variable boundaries for toString()... // Can cause infinite recursions in case of self-references return "?"; } } StringBuilder result = new StringBuilder(this.resolved.getName()); if (hasGenerics()) { result.append('<'); result.append(StringUtils.arrayToDelimitedString(getGenerics(), ", ")); result.append('>'); } return result.toString(); } // Factory methods
Return a ResolvableType for the specified Class, using the full generic type information for assignability checks. For example: ResolvableType.forClass(MyArrayList.class).
Params:
  • clazz – the class to introspect (null is semantically equivalent to Object.class for typical use cases here}
See Also:
Returns:a ResolvableType for the specified class
/** * Return a {@link ResolvableType} for the specified {@link Class}, * using the full generic type information for assignability checks. * For example: {@code ResolvableType.forClass(MyArrayList.class)}. * @param clazz the class to introspect ({@code null} is semantically * equivalent to {@code Object.class} for typical use cases here} * @return a {@link ResolvableType} for the specified class * @see #forClass(Class, Class) * @see #forClassWithGenerics(Class, Class...) */
public static ResolvableType forClass(@Nullable Class<?> clazz) { return new ResolvableType(clazz); }
Return a ResolvableType for the specified Class, doing assignability checks against the raw class only (analogous to Class.isAssignableFrom, which this serves as a wrapper for. For example: ResolvableType.forRawClass(List.class).
Params:
  • clazz – the class to introspect (null is semantically equivalent to Object.class for typical use cases here}
See Also:
Returns:a ResolvableType for the specified class
Since:4.2
/** * Return a {@link ResolvableType} for the specified {@link Class}, * doing assignability checks against the raw class only (analogous to * {@link Class#isAssignableFrom}, which this serves as a wrapper for. * For example: {@code ResolvableType.forRawClass(List.class)}. * @param clazz the class to introspect ({@code null} is semantically * equivalent to {@code Object.class} for typical use cases here} * @return a {@link ResolvableType} for the specified class * @since 4.2 * @see #forClass(Class) * @see #getRawClass() */
public static ResolvableType forRawClass(@Nullable Class<?> clazz) { return new ResolvableType(clazz) { @Override public ResolvableType[] getGenerics() { return EMPTY_TYPES_ARRAY; } @Override public boolean isAssignableFrom(Class<?> other) { return (clazz == null || ClassUtils.isAssignable(clazz, other)); } @Override public boolean isAssignableFrom(ResolvableType other) { Class<?> otherClass = other.getRawClass(); return (otherClass != null && (clazz == null || ClassUtils.isAssignable(clazz, otherClass))); } }; }
Return a ResolvableType for the specified base type (interface or base class) with a given implementation class. For example: ResolvableType.forClass(List.class, MyArrayList.class).
Params:
  • baseType – the base type (must not be null)
  • implementationClass – the implementation class
See Also:
Returns:a ResolvableType for the specified base type backed by the given implementation class
/** * Return a {@link ResolvableType} for the specified base type * (interface or base class) with a given implementation class. * For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}. * @param baseType the base type (must not be {@code null}) * @param implementationClass the implementation class * @return a {@link ResolvableType} for the specified base type backed by the * given implementation class * @see #forClass(Class) * @see #forClassWithGenerics(Class, Class...) */
public static ResolvableType forClass(Class<?> baseType, Class<?> implementationClass) { Assert.notNull(baseType, "Base type must not be null"); ResolvableType asType = forType(implementationClass).as(baseType); return (asType == NONE ? forType(baseType) : asType); }
Return a ResolvableType for the specified Class with pre-declared generics.
Params:
  • clazz – the class (or interface) to introspect
  • generics – the generics of the class
See Also:
Returns:a ResolvableType for the specific class and generics
/** * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. * @param clazz the class (or interface) to introspect * @param generics the generics of the class * @return a {@link ResolvableType} for the specific class and generics * @see #forClassWithGenerics(Class, ResolvableType...) */
public static ResolvableType forClassWithGenerics(Class<?> clazz, Class<?>... generics) { Assert.notNull(clazz, "Class must not be null"); Assert.notNull(generics, "Generics array must not be null"); ResolvableType[] resolvableGenerics = new ResolvableType[generics.length]; for (int i = 0; i < generics.length; i++) { resolvableGenerics[i] = forClass(generics[i]); } return forClassWithGenerics(clazz, resolvableGenerics); }
Return a ResolvableType for the specified Class with pre-declared generics.
Params:
  • clazz – the class (or interface) to introspect
  • generics – the generics of the class
See Also:
Returns:a ResolvableType for the specific class and generics
/** * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. * @param clazz the class (or interface) to introspect * @param generics the generics of the class * @return a {@link ResolvableType} for the specific class and generics * @see #forClassWithGenerics(Class, Class...) */
public static ResolvableType forClassWithGenerics(Class<?> clazz, ResolvableType... generics) { Assert.notNull(clazz, "Class must not be null"); Assert.notNull(generics, "Generics array must not be null"); TypeVariable<?>[] variables = clazz.getTypeParameters(); Assert.isTrue(variables.length == generics.length, "Mismatched number of generics specified"); Type[] arguments = new Type[generics.length]; for (int i = 0; i < generics.length; i++) { ResolvableType generic = generics[i]; Type argument = (generic != null ? generic.getType() : null); arguments[i] = (argument != null && !(argument instanceof TypeVariable) ? argument : variables[i]); } ParameterizedType syntheticType = new SyntheticParameterizedType(clazz, arguments); return forType(syntheticType, new TypeVariablesVariableResolver(variables, generics)); }
Return a ResolvableType for the specified instance. The instance does not convey generic information but if it implements ResolvableTypeProvider a more precise ResolvableType can be used than the simple one based on the Class instance.
Params:
  • instance – the instance
See Also:
Returns:a ResolvableType for the specified instance
Since:4.2
/** * Return a {@link ResolvableType} for the specified instance. The instance does not * convey generic information but if it implements {@link ResolvableTypeProvider} a * more precise {@link ResolvableType} can be used than the simple one based on * the {@link #forClass(Class) Class instance}. * @param instance the instance * @return a {@link ResolvableType} for the specified instance * @since 4.2 * @see ResolvableTypeProvider */
public static ResolvableType forInstance(Object instance) { Assert.notNull(instance, "Instance must not be null"); if (instance instanceof ResolvableTypeProvider) { ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType(); if (type != null) { return type; } } return ResolvableType.forClass(instance.getClass()); }
Return a ResolvableType for the specified Field.
Params:
  • field – the source field
See Also:
Returns:a ResolvableType for the specified field
/** * Return a {@link ResolvableType} for the specified {@link Field}. * @param field the source field * @return a {@link ResolvableType} for the specified field * @see #forField(Field, Class) */
public static ResolvableType forField(Field field) { Assert.notNull(field, "Field must not be null"); return forType(null, new FieldTypeProvider(field), null); }
Return a ResolvableType for the specified 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.

Params:
  • field – the source field
  • implementationClass – the implementation class
See Also:
Returns:a ResolvableType for the specified field
/** * Return a {@link ResolvableType} for the specified {@link Field} with a given * 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 * @return a {@link ResolvableType} for the specified field * @see #forField(Field) */
public static ResolvableType forField(Field field, Class<?> implementationClass) { Assert.notNull(field, "Field must not be null"); ResolvableType owner = forType(implementationClass).as(field.getDeclaringClass()); return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()); }
Return a ResolvableType for the specified 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 type.

Params:
  • field – the source field
  • implementationType – the implementation type
See Also:
Returns:a ResolvableType for the specified field
/** * Return a {@link ResolvableType} for the specified {@link Field} with a given * implementation. * <p>Use this variant when the class that declares the field includes generic * parameter variables that are satisfied by the implementation type. * @param field the source field * @param implementationType the implementation type * @return a {@link ResolvableType} for the specified field * @see #forField(Field) */
public static ResolvableType forField(Field field, @Nullable ResolvableType implementationType) { Assert.notNull(field, "Field must not be null"); ResolvableType owner = (implementationType != null ? implementationType : NONE); owner = owner.as(field.getDeclaringClass()); return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()); }
Return a ResolvableType for the specified Field with the given nesting level.
Params:
  • field – the source field
  • nestingLevel – the nesting level (1 for the outer level; 2 for a nested generic type; etc)
See Also:
/** * 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(null, new FieldTypeProvider(field), null).getNested(nestingLevel); }
Return a ResolvableType for the specified Field with a given implementation and the given nesting level.

Use this variant when the class that declares the field includes generic parameter variables that are satisfied by the implementation class.

Params:
  • field – the source field
  • nestingLevel – the nesting level (1 for the outer level; 2 for a nested generic type; etc)
  • implementationClass – the implementation class
See Also:
Returns:a ResolvableType for the specified field
/** * 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 * @return a {@link ResolvableType} for the specified field * @see #forField(Field) */
public static ResolvableType forField(Field field, int nestingLevel, @Nullable Class<?> implementationClass) { Assert.notNull(field, "Field must not be null"); ResolvableType owner = forType(implementationClass).as(field.getDeclaringClass()); return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()).getNested(nestingLevel); }
Return a ResolvableType for the specified Constructor parameter.
Params:
  • constructor – the source constructor (must not be null)
  • parameterIndex – the parameter index
See Also:
Returns:a ResolvableType for the specified constructor parameter
/** * Return a {@link ResolvableType} for the specified {@link Constructor} parameter. * @param constructor the source constructor (must not be {@code null}) * @param parameterIndex the parameter index * @return a {@link ResolvableType} for the specified constructor parameter * @see #forConstructorParameter(Constructor, int, Class) */
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) { Assert.notNull(constructor, "Constructor must not be null"); return forMethodParameter(new MethodParameter(constructor, parameterIndex)); }
Return a ResolvableType for the specified Constructor parameter with a given implementation. Use this variant when the class that declares the constructor includes generic parameter variables that are satisfied by the implementation class.
Params:
  • constructor – the source constructor (must not be null)
  • parameterIndex – the parameter index
  • implementationClass – the implementation class
See Also:
Returns:a ResolvableType for the specified constructor parameter
/** * Return a {@link ResolvableType} for the specified {@link Constructor} parameter * with a given implementation. Use this variant when the class that declares the * constructor includes generic parameter variables that are satisfied by the * implementation class. * @param constructor the source constructor (must not be {@code null}) * @param parameterIndex the parameter index * @param implementationClass the implementation class * @return a {@link ResolvableType} for the specified constructor parameter * @see #forConstructorParameter(Constructor, int) */
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex, Class<?> implementationClass) { Assert.notNull(constructor, "Constructor must not be null"); MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex); methodParameter.setContainingClass(implementationClass); return forMethodParameter(methodParameter); }
Return a ResolvableType for the specified Method return type.
Params:
  • method – the source for the method return type
See Also:
Returns:a ResolvableType for the specified 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 #forMethodReturnType(Method, Class) */
public static ResolvableType forMethodReturnType(Method method) { Assert.notNull(method, "Method must not be null"); return forMethodParameter(new MethodParameter(method, -1)); }
Return a ResolvableType for the specified Method return type. Use this variant when the class that declares the method includes generic parameter variables that are satisfied by the implementation class.
Params:
  • method – the source for the method return type
  • implementationClass – the implementation class
See Also:
Returns:a ResolvableType for the specified 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 * @return a {@link ResolvableType} for the specified method return * @see #forMethodReturnType(Method) */
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) { Assert.notNull(method, "Method must not be null"); MethodParameter methodParameter = new MethodParameter(method, -1); methodParameter.setContainingClass(implementationClass); return forMethodParameter(methodParameter); }
Return a ResolvableType for the specified Method parameter.
Params:
  • method – the source method (must not be null)
  • parameterIndex – the parameter index
See Also:
Returns:a ResolvableType for the specified method parameter
/** * Return a {@link ResolvableType} for the specified {@link Method} parameter. * @param method the source method (must not be {@code null}) * @param parameterIndex the parameter index * @return a {@link ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int, Class) * @see #forMethodParameter(MethodParameter) */
public static ResolvableType forMethodParameter(Method method, int parameterIndex) { Assert.notNull(method, "Method must not be null"); return forMethodParameter(new MethodParameter(method, parameterIndex)); }
Return a ResolvableType for the specified 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.
Params:
  • method – the source method (must not be null)
  • parameterIndex – the parameter index
  • implementationClass – the implementation class
See Also:
Returns:a ResolvableType for the specified method parameter
/** * 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. * @param method the source method (must not be {@code null}) * @param parameterIndex the parameter index * @param implementationClass the implementation class * @return a {@link ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int, Class) * @see #forMethodParameter(MethodParameter) */
public static ResolvableType forMethodParameter(Method method, int parameterIndex, Class<?> implementationClass) { Assert.notNull(method, "Method must not be null"); MethodParameter methodParameter = new MethodParameter(method, parameterIndex); methodParameter.setContainingClass(implementationClass); return forMethodParameter(methodParameter); }
Return a ResolvableType for the specified MethodParameter.
Params:
  • methodParameter – the source method parameter (must not be null)
See Also:
Returns:a ResolvableType for the specified method parameter
/** * 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(Method, int) */
public static ResolvableType forMethodParameter(MethodParameter methodParameter) { return forMethodParameter(methodParameter, (Type) null); }
Return a ResolvableType for the specified MethodParameter with a given implementation type. Use this variant when the class that declares the method includes generic parameter variables that are satisfied by the implementation type.
Params:
  • methodParameter – the source method parameter (must not be null)
  • implementationType – the implementation type
See Also:
Returns:a ResolvableType for the specified method parameter
/** * Return a {@link ResolvableType} for the specified {@link MethodParameter} with a * given implementation type. Use this variant when the class that declares the method * includes generic parameter variables that are satisfied by the implementation type. * @param methodParameter the source method parameter (must not be {@code null}) * @param implementationType the implementation type * @return a {@link ResolvableType} for the specified method parameter * @see #forMethodParameter(MethodParameter) */
public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable ResolvableType implementationType) { Assert.notNull(methodParameter, "MethodParameter must not be null"); implementationType = (implementationType != null ? implementationType : forType(methodParameter.getContainingClass())); ResolvableType owner = implementationType.as(methodParameter.getDeclaringClass()); return forType(null, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()). getNested(methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel); }
Return a ResolvableType for the specified MethodParameter, overriding the target type to resolve with a specific given type.
Params:
  • methodParameter – the source method parameter (must not be null)
  • targetType – the type to resolve (a part of the method parameter's type)
See Also:
Returns:a ResolvableType for the specified method parameter
/** * Return a {@link ResolvableType} for the specified {@link MethodParameter}, * overriding the target type to resolve with a specific given type. * @param methodParameter the source method parameter (must not be {@code null}) * @param targetType the type to resolve (a part of the method parameter's type) * @return a {@link ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int) */
public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType) { Assert.notNull(methodParameter, "MethodParameter must not be null"); ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass()); return forType(targetType, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()). getNested(methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel); }
Resolve the top-level parameter type of the given MethodParameter.
Params:
  • methodParameter – the method parameter to resolve
See Also:
Since:4.1.9
/** * Resolve the top-level parameter type of the given {@code MethodParameter}. * @param methodParameter the method parameter to resolve * @since 4.1.9 * @see MethodParameter#setParameterType */
static void resolveMethodParameter(MethodParameter methodParameter) { Assert.notNull(methodParameter, "MethodParameter must not be null"); ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass()); methodParameter.setParameterType( forType(null, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()).resolve()); }
Return a ResolvableType as a array of the specified componentType.
Params:
  • componentType – the component type
Returns:a ResolvableType as an array of the specified component type
/** * Return a {@link ResolvableType} as a array of the specified {@code componentType}. * @param componentType the component type * @return a {@link ResolvableType} as an array of the specified component type */
public static ResolvableType forArrayComponent(ResolvableType componentType) { Assert.notNull(componentType, "Component type must not be null"); Class<?> arrayClass = Array.newInstance(componentType.resolve(), 0).getClass(); return new ResolvableType(arrayClass, null, null, componentType); }
Return a ResolvableType for the specified Type.

Note: The resulting ResolvableType instance may not be Serializable.

Params:
  • type – the source type (potentially null)
See Also:
Returns:a ResolvableType for the specified Type
/** * Return a {@link ResolvableType} for the specified {@link Type}. * <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. * @param type the source type (potentially {@code null}) * @return a {@link ResolvableType} for the specified {@link Type} * @see #forType(Type, ResolvableType) */
public static ResolvableType forType(@Nullable Type type) { return forType(type, null, null); }
Return a ResolvableType for the specified Type backed by the given owner type.

Note: The resulting ResolvableType instance may not be Serializable.

Params:
  • type – the source type or null
  • owner – the owner type used to resolve variables
See Also:
Returns:a ResolvableType for the specified Type and owner
/** * Return a {@link ResolvableType} for the specified {@link Type} backed by the given * owner type. * <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. * @param type the source type or {@code null} * @param owner the owner type used to resolve variables * @return a {@link ResolvableType} for the specified {@link Type} and owner * @see #forType(Type) */
public static ResolvableType forType(@Nullable Type type, @Nullable ResolvableType owner) { VariableResolver variableResolver = null; if (owner != null) { variableResolver = owner.asVariableResolver(); } return forType(type, variableResolver); }
Return a ResolvableType for the specified ParameterizedTypeReference.

Note: The resulting ResolvableType instance may not be Serializable.

Params:
  • typeReference – the reference to obtain the source type from
See Also:
Returns:a ResolvableType for the specified ParameterizedTypeReference
Since:4.3.12
/** * Return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}. * <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. * @param typeReference the reference to obtain the source type from * @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference} * @since 4.3.12 * @see #forType(Type) */
public static ResolvableType forType(ParameterizedTypeReference<?> typeReference) { return forType(typeReference.getType(), null, null); }
Return a ResolvableType for the specified Type backed by a given VariableResolver.
Params:
  • type – the source type or null
  • variableResolver – the variable resolver or null
Returns:a ResolvableType for the specified Type and VariableResolver
/** * Return a {@link ResolvableType} for the specified {@link Type} backed by a given * {@link VariableResolver}. * @param type the source type or {@code null} * @param variableResolver the variable resolver or {@code null} * @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} */
static ResolvableType forType(@Nullable Type type, @Nullable VariableResolver variableResolver) { return forType(type, null, variableResolver); }
Return a ResolvableType for the specified Type backed by a given VariableResolver.
Params:
  • type – the source type or null
  • typeProvider – the type provider or null
  • variableResolver – the variable resolver or null
Returns:a ResolvableType for the specified Type and VariableResolver
/** * Return a {@link ResolvableType} for the specified {@link Type} backed by a given * {@link VariableResolver}. * @param type the source type or {@code null} * @param typeProvider the type provider or {@code null} * @param variableResolver the variable resolver or {@code null} * @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} */
static ResolvableType forType( @Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { if (type == null && typeProvider != null) { type = SerializableTypeWrapper.forTypeProvider(typeProvider); } if (type == null) { return NONE; } // For simple Class references, build the wrapper right away - // no expensive resolution necessary, so not worth caching... if (type instanceof Class) { return new ResolvableType(type, typeProvider, variableResolver, (ResolvableType) null); } // Purge empty entries on access since we don't have a clean-up thread or the like. cache.purgeUnreferencedEntries(); // Check the cache - we may have a ResolvableType which has been resolved before... ResolvableType resultType = new ResolvableType(type, typeProvider, variableResolver); ResolvableType cachedType = cache.get(resultType); if (cachedType == null) { cachedType = new ResolvableType(type, typeProvider, variableResolver, resultType.hash); cache.put(cachedType, cachedType); } resultType.resolved = cachedType.resolved; return resultType; }
Clear the internal ResolvableType/SerializableTypeWrapper cache.
Since:4.2
/** * Clear the internal {@code ResolvableType}/{@code SerializableTypeWrapper} cache. * @since 4.2 */
public static void clearCache() { cache.clear(); SerializableTypeWrapper.cache.clear(); }
Strategy interface used to resolve TypeVariables.
/** * Strategy interface used to resolve {@link TypeVariable TypeVariables}. */
interface VariableResolver extends Serializable {
Return the source of the resolver (used for hashCode and equals).
/** * Return the source of the resolver (used for hashCode and equals). */
Object getSource();
Resolve the specified variable.
Params:
  • variable – the variable to resolve
Returns:the resolved variable, or null if not found
/** * Resolve the specified variable. * @param variable the variable to resolve * @return the resolved variable, or {@code null} if not found */
@Nullable ResolvableType resolveVariable(TypeVariable<?> variable); } @SuppressWarnings("serial") private class DefaultVariableResolver implements VariableResolver { @Override @Nullable public ResolvableType resolveVariable(TypeVariable<?> variable) { return ResolvableType.this.resolveVariable(variable); } @Override public Object getSource() { return ResolvableType.this; } } @SuppressWarnings("serial") private static class TypeVariablesVariableResolver implements VariableResolver { private final TypeVariable<?>[] variables; private final ResolvableType[] generics; public TypeVariablesVariableResolver(TypeVariable<?>[] variables, ResolvableType[] generics) { this.variables = variables; this.generics = generics; } @Override @Nullable public ResolvableType resolveVariable(TypeVariable<?> variable) { for (int i = 0; i < this.variables.length; i++) { TypeVariable<?> v1 = SerializableTypeWrapper.unwrap(this.variables[i]); TypeVariable<?> v2 = SerializableTypeWrapper.unwrap(variable); if (ObjectUtils.nullSafeEquals(v1, v2)) { return this.generics[i]; } } return null; } @Override public Object getSource() { return this.generics; } } private static final class SyntheticParameterizedType implements ParameterizedType, Serializable { private final Type rawType; private final Type[] typeArguments; public SyntheticParameterizedType(Type rawType, Type[] typeArguments) { this.rawType = rawType; this.typeArguments = typeArguments; } @Override public String getTypeName() { StringBuilder result = new StringBuilder(this.rawType.getTypeName()); if (this.typeArguments.length > 0) { result.append('<'); for (int i = 0; i < this.typeArguments.length; i++) { if (i > 0) { result.append(", "); } result.append(this.typeArguments[i].getTypeName()); } result.append('>'); } return result.toString(); } @Override @Nullable public Type getOwnerType() { return null; } @Override public Type getRawType() { return this.rawType; } @Override public Type[] getActualTypeArguments() { return this.typeArguments; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof ParameterizedType)) { return false; } ParameterizedType otherType = (ParameterizedType) other; return (otherType.getOwnerType() == null && this.rawType.equals(otherType.getRawType()) && Arrays.equals(this.typeArguments, otherType.getActualTypeArguments())); } @Override public int hashCode() { return (this.rawType.hashCode() * 31 + Arrays.hashCode(this.typeArguments)); } @Override public String toString() { return getTypeName(); } }
Internal helper to handle bounds from WildcardTypes.
/** * Internal helper to handle bounds from {@link WildcardType WildcardTypes}. */
private static class WildcardBounds { private final Kind kind; private final ResolvableType[] bounds;
Internal constructor to create a new WildcardBounds instance.
Params:
  • kind – the kind of bounds
  • bounds – the bounds
See Also:
/** * Internal constructor to create a new {@link WildcardBounds} instance. * @param kind the kind of bounds * @param bounds the bounds * @see #get(ResolvableType) */
public WildcardBounds(Kind kind, ResolvableType[] bounds) { this.kind = kind; this.bounds = bounds; }
Return true if this bounds is the same kind as the specified bounds.
/** * Return {@code true} if this bounds is the same kind as the specified bounds. */
public boolean isSameKind(WildcardBounds bounds) { return this.kind == bounds.kind; }
Return true if this bounds is assignable to all the specified types.
Params:
  • types – the types to test against
Returns:true if this bounds is assignable to all types
/** * Return {@code true} if this bounds is assignable to all the specified types. * @param types the types to test against * @return {@code true} if this bounds is assignable to all types */
public boolean isAssignableFrom(ResolvableType... types) { for (ResolvableType bound : this.bounds) { for (ResolvableType type : types) { if (!isAssignable(bound, type)) { return false; } } } return true; } private boolean isAssignable(ResolvableType source, ResolvableType from) { return (this.kind == Kind.UPPER ? source.isAssignableFrom(from) : from.isAssignableFrom(source)); }
Return the underlying bounds.
/** * Return the underlying bounds. */
public ResolvableType[] getBounds() { return this.bounds; }
Get a WildcardBounds instance for the specified type, returning null if the specified type cannot be resolved to a WildcardType.
Params:
  • type – the source type
Returns:a WildcardBounds instance or null
/** * Get a {@link WildcardBounds} instance for the specified type, returning * {@code null} if the specified type cannot be resolved to a {@link WildcardType}. * @param type the source type * @return a {@link WildcardBounds} instance or {@code null} */
@Nullable public static WildcardBounds get(ResolvableType type) { ResolvableType resolveToWildcard = type; 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()); ResolvableType[] resolvableBounds = new ResolvableType[bounds.length]; for (int i = 0; i < bounds.length; i++) { resolvableBounds[i] = ResolvableType.forType(bounds[i], type.variableResolver); } return new WildcardBounds(boundsType, resolvableBounds); }
The various kinds of bounds.
/** * The various kinds of bounds. */
enum Kind {UPPER, LOWER} }
Internal Type used to represent an empty value.
/** * Internal {@link Type} used to represent an empty value. */
@SuppressWarnings("serial") static class EmptyType implements Type, Serializable { static final Type INSTANCE = new EmptyType(); Object readResolve() { return INSTANCE; } } }