Revise ResolvableType equality

ResolvableTypes are only considered equal if of the very same class now.
As a consequence, a forRawClass result is not equal to forClass anymore.
The new equalsType method is available for plain type equality checks.

Closes gh-28608
Closes gh-27748
This commit is contained in:
Juergen Hoeller
2023-07-09 12:35:37 +02:00
parent 5243c2262a
commit 6b1fbc9fe1
2 changed files with 152 additions and 113 deletions

View File

@@ -100,6 +100,12 @@ public class ResolvableType implements Serializable {
*/ */
private final Type type; private final Type type;
/**
* The component type for an array or {@code null} if the type should be deduced.
*/
@Nullable
private final ResolvableType componentType;
/** /**
* Optional provider for the type. * Optional provider for the type.
*/ */
@@ -112,12 +118,6 @@ public class ResolvableType implements Serializable {
@Nullable @Nullable
private final VariableResolver variableResolver; private final VariableResolver variableResolver;
/**
* The component type for an array or {@code null} if the type should be deduced.
*/
@Nullable
private final ResolvableType componentType;
@Nullable @Nullable
private final Integer hash; private final Integer hash;
@@ -138,22 +138,22 @@ public class ResolvableType implements Serializable {
/** /**
* Private constructor used to create a new {@link ResolvableType} for cache key purposes, * Private constructor used to create a new {@code ResolvableType} for cache key purposes,
* with no upfront resolution. * with no upfront resolution.
*/ */
private ResolvableType( private ResolvableType(
Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
this.type = type; this.type = type;
this.componentType = null;
this.typeProvider = typeProvider; this.typeProvider = typeProvider;
this.variableResolver = variableResolver; this.variableResolver = variableResolver;
this.componentType = null;
this.hash = calculateHashCode(); this.hash = calculateHashCode();
this.resolved = null; this.resolved = null;
} }
/** /**
* Private constructor used to create a new {@link ResolvableType} for cache value purposes, * Private constructor used to create a new {@code ResolvableType} for cache value purposes,
* with upfront resolution and a pre-calculated hash. * with upfront resolution and a pre-calculated hash.
* @since 4.2 * @since 4.2
*/ */
@@ -161,39 +161,39 @@ public class ResolvableType implements Serializable {
@Nullable VariableResolver variableResolver, @Nullable Integer hash) { @Nullable VariableResolver variableResolver, @Nullable Integer hash) {
this.type = type; this.type = type;
this.componentType = null;
this.typeProvider = typeProvider; this.typeProvider = typeProvider;
this.variableResolver = variableResolver; this.variableResolver = variableResolver;
this.componentType = null;
this.hash = hash; this.hash = hash;
this.resolved = resolveClass(); this.resolved = resolveClass();
} }
/** /**
* Private constructor used to create a new {@link ResolvableType} for uncached purposes, * Private constructor used to create a new {@code ResolvableType} for uncached purposes,
* with upfront resolution but lazily calculated hash. * with upfront resolution but lazily calculated hash.
*/ */
private ResolvableType(Type type, @Nullable TypeProvider typeProvider, private ResolvableType(Type type, @Nullable ResolvableType componentType,
@Nullable VariableResolver variableResolver, @Nullable ResolvableType componentType) { @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
this.type = type; this.type = type;
this.componentType = componentType;
this.typeProvider = typeProvider; this.typeProvider = typeProvider;
this.variableResolver = variableResolver; this.variableResolver = variableResolver;
this.componentType = componentType;
this.hash = null; this.hash = null;
this.resolved = resolveClass(); this.resolved = resolveClass();
} }
/** /**
* Private constructor used to create a new {@link ResolvableType} on a {@link Class} basis. * Private constructor used to create a new {@code ResolvableType} on a {@link Class} basis.
* <p>Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper. * <p>Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper.
* @since 4.2 * @since 4.2
*/ */
private ResolvableType(@Nullable Class<?> clazz) { private ResolvableType(@Nullable Class<?> clazz) {
this.resolved = (clazz != null ? clazz : Object.class); this.resolved = (clazz != null ? clazz : Object.class);
this.type = this.resolved; this.type = this.resolved;
this.componentType = null;
this.typeProvider = null; this.typeProvider = null;
this.variableResolver = null; this.variableResolver = null;
this.componentType = null;
this.hash = null; this.hash = null;
} }
@@ -223,7 +223,7 @@ public class ResolvableType implements Serializable {
/** /**
* Return the underlying source of the resolvable type. Will return a {@link Field}, * Return the underlying source of the resolvable type. Will return a {@link Field},
* {@link MethodParameter} or {@link Type} depending on how the {@link ResolvableType} * {@link MethodParameter} or {@link Type} depending on how the {@code ResolvableType}
* was constructed. This method is primarily to provide access to additional type * was constructed. This method is primarily to provide access to additional type
* information or meta-data that alternative JVM languages may provide. * information or meta-data that alternative JVM languages may provide.
*/ */
@@ -428,12 +428,12 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return this type as a {@link ResolvableType} of the specified class. Searches * Return this type as a {@code ResolvableType} of the specified class. Searches
* {@link #getSuperType() supertype} and {@link #getInterfaces() interface} * {@link #getSuperType() supertype} and {@link #getInterfaces() interface}
* hierarchies to find a match, returning {@link #NONE} if this type does not * hierarchies to find a match, returning {@link #NONE} if this type does not
* implement or extend the specified class. * implement or extend the specified class.
* @param type the required type (typically narrowed) * @param type the required type (typically narrowed)
* @return a {@link ResolvableType} representing this object as the specified * @return a {@code ResolvableType} representing this object as the specified
* type, or {@link #NONE} if not resolvable as that type * type, or {@link #NONE} if not resolvable as that type
* @see #asCollection() * @see #asCollection()
* @see #asMap() * @see #asMap()
@@ -458,9 +458,9 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} representing the direct supertype of this type. * Return a {@code ResolvableType} representing the direct supertype of this type.
* <p>If no supertype is available this method returns {@link #NONE}. * <p>If no supertype is available this method returns {@link #NONE}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. * <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
* @see #getInterfaces() * @see #getInterfaces()
*/ */
public ResolvableType getSuperType() { public ResolvableType getSuperType() {
@@ -487,10 +487,10 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} array representing the direct interfaces * Return a {@code ResolvableType} array representing the direct interfaces
* implemented by this type. If this type does not implement any interfaces an * implemented by this type. If this type does not implement any interfaces an
* empty array is returned. * empty array is returned.
* <p>Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}. * <p>Note: The resulting {@code ResolvableType} instances may not be {@link Serializable}.
* @see #getSuperType() * @see #getSuperType()
*/ */
public ResolvableType[] getInterfaces() { public ResolvableType[] getInterfaces() {
@@ -618,17 +618,17 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified nesting level. * Return a {@code ResolvableType} for the specified nesting level.
* <p>See {@link #getNested(int, Map)} for details. * <p>See {@link #getNested(int, Map)} for details.
* @param nestingLevel the nesting level * @param nestingLevel the nesting level
* @return the {@link ResolvableType} type, or {@code #NONE} * @return the {@code ResolvableType} type, or {@code #NONE}
*/ */
public ResolvableType getNested(int nestingLevel) { public ResolvableType getNested(int nestingLevel) {
return getNested(nestingLevel, null); return getNested(nestingLevel, null);
} }
/** /**
* Return a {@link ResolvableType} for the specified nesting level. * Return a {@code ResolvableType} for the specified nesting level.
* <p>The nesting level refers to the specific generic parameter that should be returned. * <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; * 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 * 3 the second; and so on. For example, given {@code List<Set<Integer>>} level 1 refers
@@ -645,7 +645,7 @@ public class ResolvableType implements Serializable {
* current type, 2 for the first nested generic, 3 for the second and so on * 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 * @param typeIndexesPerLevel a map containing the generic index for a given
* nesting level (may be {@code null}) * nesting level (may be {@code null})
* @return a {@link ResolvableType} for the nested level, or {@link #NONE} * @return a {@code ResolvableType} for the nested level, or {@link #NONE}
*/ */
public ResolvableType getNested(int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel) { public ResolvableType getNested(int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel) {
ResolvableType result = this; ResolvableType result = this;
@@ -667,7 +667,7 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} representing the generic parameter for the * Return a {@code ResolvableType} representing the generic parameter for the
* given indexes. Indexes are zero based; for example given the type * given indexes. Indexes are zero based; for example given the type
* {@code Map<Integer, List<String>>}, {@code getGeneric(0)} will access the * {@code Map<Integer, List<String>>}, {@code getGeneric(0)} will access the
* {@code Integer}. Nested generics can be accessed by specifying multiple indexes; * {@code Integer}. Nested generics can be accessed by specifying multiple indexes;
@@ -677,7 +677,7 @@ public class ResolvableType implements Serializable {
* <p>If no generic is available at the specified indexes {@link #NONE} 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 * @param indexes the indexes that refer to the generic parameter
* (may be omitted to return the first generic) * (may be omitted to return the first generic)
* @return a {@link ResolvableType} for the specified generic, or {@link #NONE} * @return a {@code ResolvableType} for the specified generic, or {@link #NONE}
* @see #hasGenerics() * @see #hasGenerics()
* @see #getGenerics() * @see #getGenerics()
* @see #resolveGeneric(int...) * @see #resolveGeneric(int...)
@@ -839,7 +839,7 @@ public class ResolvableType implements Serializable {
/** /**
* Resolve this type by a single level, returning the resolved value or {@link #NONE}. * 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 * <p>Note: The returned {@code ResolvableType} should only be used as an intermediary
* as it cannot be serialized. * as it cannot be serialized.
*/ */
ResolvableType resolveType() { ResolvableType resolveType() {
@@ -910,16 +910,22 @@ public class ResolvableType implements Serializable {
} }
/**
* Check for full equality of all type resolution artifacts:
* type as well as {@code TypeProvider} and {@code VariableResolver}.
* @see #equalsType(ResolvableType)
*/
@Override @Override
public boolean equals(@Nullable Object other) { public boolean equals(@Nullable Object other) {
if (this == other) { if (this == other) {
return true; return true;
} }
if (!(other instanceof ResolvableType otherType)) { if (other == null || other.getClass() != getClass()) {
return false; return false;
} }
ResolvableType otherType = (ResolvableType) other;
if (!ObjectUtils.nullSafeEquals(this.type, otherType.type)) { if (!equalsType(otherType)) {
return false; return false;
} }
if (this.typeProvider != otherType.typeProvider && if (this.typeProvider != otherType.typeProvider &&
@@ -932,12 +938,22 @@ public class ResolvableType implements Serializable {
!ObjectUtils.nullSafeEquals(this.variableResolver.getSource(), otherType.variableResolver.getSource()))) { !ObjectUtils.nullSafeEquals(this.variableResolver.getSource(), otherType.variableResolver.getSource()))) {
return false; return false;
} }
if (!ObjectUtils.nullSafeEquals(this.componentType, otherType.componentType)) {
return false;
}
return true; return true;
} }
/**
* Check for type-level equality with another {@code ResolvableType}.
* <p>In contrast to {@link #equals(Object)} or {@link #isAssignableFrom(ResolvableType)},
* this works between different sources as well, e.g. method parameters and return types.
* @param otherType the {@code ResolvableType} to match against
* @return whether the declared type and type variables match
* @since 6.1
*/
public boolean equalsType(ResolvableType otherType) {
return (ObjectUtils.nullSafeEquals(this.type, otherType.type) &&
ObjectUtils.nullSafeEquals(this.componentType, otherType.componentType));
}
@Override @Override
public int hashCode() { public int hashCode() {
return (this.hash != null ? this.hash : calculateHashCode()); return (this.hash != null ? this.hash : calculateHashCode());
@@ -945,20 +961,20 @@ public class ResolvableType implements Serializable {
private int calculateHashCode() { private int calculateHashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(this.type); int hashCode = ObjectUtils.nullSafeHashCode(this.type);
if (this.componentType != null) {
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.componentType);
}
if (this.typeProvider != null) { if (this.typeProvider != null) {
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.typeProvider.getType()); hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.typeProvider.getType());
} }
if (this.variableResolver != null) { if (this.variableResolver != null) {
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.variableResolver.getSource()); hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.variableResolver.getSource());
} }
if (this.componentType != null) {
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.componentType);
}
return hashCode; return hashCode;
} }
/** /**
* Adapts this {@link ResolvableType} to a {@link VariableResolver}. * Adapts this {@code ResolvableType} to a {@link VariableResolver}.
*/ */
@Nullable @Nullable
VariableResolver asVariableResolver() { VariableResolver asVariableResolver() {
@@ -1004,12 +1020,12 @@ public class ResolvableType implements Serializable {
// Factory methods // Factory methods
/** /**
* Return a {@link ResolvableType} for the specified {@link Class}, * Return a {@code ResolvableType} for the specified {@link Class},
* using the full generic type information for assignability checks. * using the full generic type information for assignability checks.
* <p>For example: {@code ResolvableType.forClass(MyArrayList.class)}. * <p>For example: {@code ResolvableType.forClass(MyArrayList.class)}.
* @param clazz the class to introspect ({@code null} is semantically * @param clazz the class to introspect ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here) * equivalent to {@code Object.class} for typical use cases here)
* @return a {@link ResolvableType} for the specified class * @return a {@code ResolvableType} for the specified class
* @see #forClass(Class, Class) * @see #forClass(Class, Class)
* @see #forClassWithGenerics(Class, Class...) * @see #forClassWithGenerics(Class, Class...)
*/ */
@@ -1018,13 +1034,13 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Class}, * Return a {@code ResolvableType} for the specified {@link Class},
* doing assignability checks against the raw class only (analogous to * doing assignability checks against the raw class only (analogous to
* {@link Class#isAssignableFrom}, which this serves as a wrapper for). * {@link Class#isAssignableFrom}, which this serves as a wrapper for).
* <p>For example: {@code ResolvableType.forRawClass(List.class)}. * <p>For example: {@code ResolvableType.forRawClass(List.class)}.
* @param clazz the class to introspect ({@code null} is semantically * @param clazz the class to introspect ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here) * equivalent to {@code Object.class} for typical use cases here)
* @return a {@link ResolvableType} for the specified class * @return a {@code ResolvableType} for the specified class
* @since 4.2 * @since 4.2
* @see #forClass(Class) * @see #forClass(Class)
* @see #getRawClass() * @see #getRawClass()
@@ -1048,12 +1064,12 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified base type * Return a {@code ResolvableType} for the specified base type
* (interface or base class) with a given implementation class. * (interface or base class) with a given implementation class.
* <p>For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}. * <p>For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}.
* @param baseType the base type (must not be {@code null}) * @param baseType the base type (must not be {@code null})
* @param implementationClass the implementation class * @param implementationClass the implementation class
* @return a {@link ResolvableType} for the specified base type backed by the * @return a {@code ResolvableType} for the specified base type backed by the
* given implementation class * given implementation class
* @see #forClass(Class) * @see #forClass(Class)
* @see #forClassWithGenerics(Class, Class...) * @see #forClassWithGenerics(Class, Class...)
@@ -1065,10 +1081,10 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. * Return a {@code ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param clazz the class (or interface) to introspect * @param clazz the class (or interface) to introspect
* @param generics the generics of the class * @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics * @return a {@code ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, ResolvableType...) * @see #forClassWithGenerics(Class, ResolvableType...)
*/ */
public static ResolvableType forClassWithGenerics(Class<?> clazz, Class<?>... generics) { public static ResolvableType forClassWithGenerics(Class<?> clazz, Class<?>... generics) {
@@ -1082,10 +1098,10 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. * Return a {@code ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param clazz the class (or interface) to introspect * @param clazz the class (or interface) to introspect
* @param generics the generics of the class * @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics * @return a {@code ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, Class...) * @see #forClassWithGenerics(Class, Class...)
*/ */
public static ResolvableType forClassWithGenerics(Class<?> clazz, ResolvableType... generics) { public static ResolvableType forClassWithGenerics(Class<?> clazz, ResolvableType... generics) {
@@ -1106,12 +1122,12 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified instance. The instance does not * Return a {@code ResolvableType} for the specified instance. The instance does not
* convey generic information but if it implements {@link ResolvableTypeProvider} a * convey generic information but if it implements {@link ResolvableTypeProvider} a
* more precise {@link ResolvableType} can be used than the simple one based on * more precise {@code ResolvableType} can be used than the simple one based on
* the {@link #forClass(Class) Class instance}. * the {@link #forClass(Class) Class instance}.
* @param instance the instance (possibly {@code null}) * @param instance the instance (possibly {@code null})
* @return a {@link ResolvableType} for the specified instance, * @return a {@code ResolvableType} for the specified instance,
* or {@code NONE} for {@code null} * or {@code NONE} for {@code null}
* @since 4.2 * @since 4.2
* @see ResolvableTypeProvider * @see ResolvableTypeProvider
@@ -1127,9 +1143,9 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Field}. * Return a {@code ResolvableType} for the specified {@link Field}.
* @param field the source field * @param field the source field
* @return a {@link ResolvableType} for the specified field * @return a {@code ResolvableType} for the specified field
* @see #forField(Field, Class) * @see #forField(Field, Class)
*/ */
public static ResolvableType forField(Field field) { public static ResolvableType forField(Field field) {
@@ -1138,13 +1154,13 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Field} with a given * Return a {@code ResolvableType} for the specified {@link Field} with a given
* implementation. * implementation.
* <p>Use this variant when the class that declares the field includes generic * <p>Use this variant when the class that declares the field includes generic
* parameter variables that are satisfied by the implementation class. * parameter variables that are satisfied by the implementation class.
* @param field the source field * @param field the source field
* @param implementationClass the implementation class * @param implementationClass the implementation class
* @return a {@link ResolvableType} for the specified field * @return a {@code ResolvableType} for the specified field
* @see #forField(Field) * @see #forField(Field)
*/ */
public static ResolvableType forField(Field field, Class<?> implementationClass) { public static ResolvableType forField(Field field, Class<?> implementationClass) {
@@ -1154,13 +1170,13 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Field} with a given * Return a {@code ResolvableType} for the specified {@link Field} with a given
* implementation. * implementation.
* <p>Use this variant when the class that declares the field includes generic * <p>Use this variant when the class that declares the field includes generic
* parameter variables that are satisfied by the implementation type. * parameter variables that are satisfied by the implementation type.
* @param field the source field * @param field the source field
* @param implementationType the implementation type * @param implementationType the implementation type
* @return a {@link ResolvableType} for the specified field * @return a {@code ResolvableType} for the specified field
* @see #forField(Field) * @see #forField(Field)
*/ */
public static ResolvableType forField(Field field, @Nullable ResolvableType implementationType) { public static ResolvableType forField(Field field, @Nullable ResolvableType implementationType) {
@@ -1171,7 +1187,7 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Field} with the * Return a {@code ResolvableType} for the specified {@link Field} with the
* given nesting level. * given nesting level.
* @param field the source field * @param field the source field
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested * @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
@@ -1184,7 +1200,7 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Field} with a given * Return a {@code ResolvableType} for the specified {@link Field} with a given
* implementation and the given nesting level. * implementation and the given nesting level.
* <p>Use this variant when the class that declares the field includes generic * <p>Use this variant when the class that declares the field includes generic
* parameter variables that are satisfied by the implementation class. * parameter variables that are satisfied by the implementation class.
@@ -1192,7 +1208,7 @@ public class ResolvableType implements Serializable {
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested * @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
* generic type; etc) * generic type; etc)
* @param implementationClass the implementation class * @param implementationClass the implementation class
* @return a {@link ResolvableType} for the specified field * @return a {@code ResolvableType} for the specified field
* @see #forField(Field) * @see #forField(Field)
*/ */
public static ResolvableType forField(Field field, int nestingLevel, @Nullable Class<?> implementationClass) { public static ResolvableType forField(Field field, int nestingLevel, @Nullable Class<?> implementationClass) {
@@ -1202,10 +1218,10 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter. * Return a {@code ResolvableType} for the specified {@link Constructor} parameter.
* @param constructor the source constructor (must not be {@code null}) * @param constructor the source constructor (must not be {@code null})
* @param parameterIndex the parameter index * @param parameterIndex the parameter index
* @return a {@link ResolvableType} for the specified constructor parameter * @return a {@code ResolvableType} for the specified constructor parameter
* @see #forConstructorParameter(Constructor, int, Class) * @see #forConstructorParameter(Constructor, int, Class)
*/ */
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) { public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) {
@@ -1214,14 +1230,14 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter * Return a {@code ResolvableType} for the specified {@link Constructor} parameter
* with a given implementation. Use this variant when the class that declares the * with a given implementation. Use this variant when the class that declares the
* constructor includes generic parameter variables that are satisfied by the * constructor includes generic parameter variables that are satisfied by the
* implementation class. * implementation class.
* @param constructor the source constructor (must not be {@code null}) * @param constructor the source constructor (must not be {@code null})
* @param parameterIndex the parameter index * @param parameterIndex the parameter index
* @param implementationClass the implementation class * @param implementationClass the implementation class
* @return a {@link ResolvableType} for the specified constructor parameter * @return a {@code ResolvableType} for the specified constructor parameter
* @see #forConstructorParameter(Constructor, int) * @see #forConstructorParameter(Constructor, int)
*/ */
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex, public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
@@ -1233,9 +1249,9 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Method} return type. * Return a {@code ResolvableType} for the specified {@link Method} return type.
* @param method the source for the method return type * @param method the source for the method return type
* @return a {@link ResolvableType} for the specified method return * @return a {@code ResolvableType} for the specified method return
* @see #forMethodReturnType(Method, Class) * @see #forMethodReturnType(Method, Class)
*/ */
public static ResolvableType forMethodReturnType(Method method) { public static ResolvableType forMethodReturnType(Method method) {
@@ -1244,12 +1260,12 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Method} return type. * Return a {@code ResolvableType} for the specified {@link Method} return type.
* <p>Use this variant when the class that declares the method includes generic * <p>Use this variant when the class that declares the method includes generic
* parameter variables that are satisfied by the implementation class. * parameter variables that are satisfied by the implementation class.
* @param method the source for the method return type * @param method the source for the method return type
* @param implementationClass the implementation class * @param implementationClass the implementation class
* @return a {@link ResolvableType} for the specified method return * @return a {@code ResolvableType} for the specified method return
* @see #forMethodReturnType(Method) * @see #forMethodReturnType(Method)
*/ */
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) { public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
@@ -1259,10 +1275,10 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Method} parameter. * Return a {@code ResolvableType} for the specified {@link Method} parameter.
* @param method the source method (must not be {@code null}) * @param method the source method (must not be {@code null})
* @param parameterIndex the parameter index * @param parameterIndex the parameter index
* @return a {@link ResolvableType} for the specified method parameter * @return a {@code ResolvableType} for the specified method parameter
* @see #forMethodParameter(Method, int, Class) * @see #forMethodParameter(Method, int, Class)
* @see #forMethodParameter(MethodParameter) * @see #forMethodParameter(MethodParameter)
*/ */
@@ -1272,13 +1288,13 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Method} parameter with a * Return a {@code ResolvableType} for the specified {@link Method} parameter with a
* given implementation. Use this variant when the class that declares the method * 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 method the source method (must not be {@code null})
* @param parameterIndex the parameter index * @param parameterIndex the parameter index
* @param implementationClass the implementation class * @param implementationClass the implementation class
* @return a {@link ResolvableType} for the specified method parameter * @return a {@code ResolvableType} for the specified method parameter
* @see #forMethodParameter(Method, int, Class) * @see #forMethodParameter(Method, int, Class)
* @see #forMethodParameter(MethodParameter) * @see #forMethodParameter(MethodParameter)
*/ */
@@ -1289,9 +1305,9 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link MethodParameter}. * Return a {@code ResolvableType} for the specified {@link MethodParameter}.
* @param methodParameter the source method parameter (must not be {@code null}) * @param methodParameter the source method parameter (must not be {@code null})
* @return a {@link ResolvableType} for the specified method parameter * @return a {@code ResolvableType} for the specified method parameter
* @see #forMethodParameter(Method, int) * @see #forMethodParameter(Method, int)
*/ */
public static ResolvableType forMethodParameter(MethodParameter methodParameter) { public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
@@ -1299,12 +1315,12 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link MethodParameter} with a * Return a {@code ResolvableType} for the specified {@link MethodParameter} with a
* given implementation type. Use this variant when the class that declares the method * given implementation type. Use this variant when the class that declares the method
* includes generic parameter variables that are satisfied by the implementation type. * includes generic parameter variables that are satisfied by the implementation type.
* @param methodParameter the source method parameter (must not be {@code null}) * @param methodParameter the source method parameter (must not be {@code null})
* @param implementationType the implementation type * @param implementationType the implementation type
* @return a {@link ResolvableType} for the specified method parameter * @return a {@code ResolvableType} for the specified method parameter
* @see #forMethodParameter(MethodParameter) * @see #forMethodParameter(MethodParameter)
*/ */
public static ResolvableType forMethodParameter(MethodParameter methodParameter, public static ResolvableType forMethodParameter(MethodParameter methodParameter,
@@ -1319,11 +1335,11 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link MethodParameter}, * Return a {@code ResolvableType} for the specified {@link MethodParameter},
* overriding the target type to resolve with a specific given type. * overriding the target type to resolve with a specific given type.
* @param methodParameter the source method parameter (must not be {@code null}) * @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) * @param targetType the type to resolve (a part of the method parameter's type)
* @return a {@link ResolvableType} for the specified method parameter * @return a {@code ResolvableType} for the specified method parameter
* @see #forMethodParameter(Method, int) * @see #forMethodParameter(Method, int)
*/ */
public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType) { public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType) {
@@ -1332,13 +1348,13 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link MethodParameter} at * Return a {@code ResolvableType} for the specified {@link MethodParameter} at
* a specific nesting level, overriding the target type to resolve with a specific * a specific nesting level, overriding the target type to resolve with a specific
* given type. * given type.
* @param methodParameter the source method parameter (must not be {@code null}) * @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) * @param targetType the type to resolve (a part of the method parameter's type)
* @param nestingLevel the nesting level to use * @param nestingLevel the nesting level to use
* @return a {@link ResolvableType} for the specified method parameter * @return a {@code ResolvableType} for the specified method parameter
* @since 5.2 * @since 5.2
* @see #forMethodParameter(Method, int) * @see #forMethodParameter(Method, int)
*/ */
@@ -1351,21 +1367,21 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} as an array of the specified {@code componentType}. * Return a {@code ResolvableType} as an array of the specified {@code componentType}.
* @param componentType the component type * @param componentType the component type
* @return a {@link ResolvableType} as an array of the specified component type * @return a {@code ResolvableType} as an array of the specified component type
*/ */
public static ResolvableType forArrayComponent(ResolvableType componentType) { public static ResolvableType forArrayComponent(ResolvableType componentType) {
Assert.notNull(componentType, "Component type must not be null"); Assert.notNull(componentType, "Component type must not be null");
Class<?> arrayClass = Array.newInstance(componentType.resolve(), 0).getClass(); Class<?> arrayClass = Array.newInstance(componentType.resolve(), 0).getClass();
return new ResolvableType(arrayClass, null, null, componentType); return new ResolvableType(arrayClass, componentType, null, null);
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Type}. * Return a {@code ResolvableType} for the specified {@link Type}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. * <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
* @param type the source type (potentially {@code null}) * @param type the source type (potentially {@code null})
* @return a {@link ResolvableType} for the specified {@link Type} * @return a {@code ResolvableType} for the specified {@link Type}
* @see #forType(Type, ResolvableType) * @see #forType(Type, ResolvableType)
*/ */
public static ResolvableType forType(@Nullable Type type) { public static ResolvableType forType(@Nullable Type type) {
@@ -1373,12 +1389,12 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Type} backed by the given * Return a {@code ResolvableType} for the specified {@link Type} backed by the given
* owner type. * owner type.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. * <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
* @param type the source type or {@code null} * @param type the source type or {@code null}
* @param owner the owner type used to resolve variables * @param owner the owner type used to resolve variables
* @return a {@link ResolvableType} for the specified {@link Type} and owner * @return a {@code ResolvableType} for the specified {@link Type} and owner
* @see #forType(Type) * @see #forType(Type)
*/ */
public static ResolvableType forType(@Nullable Type type, @Nullable ResolvableType owner) { public static ResolvableType forType(@Nullable Type type, @Nullable ResolvableType owner) {
@@ -1391,10 +1407,10 @@ public class ResolvableType implements Serializable {
/** /**
* Return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}. * Return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. * <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
* @param typeReference the reference to obtain the source type from * @param typeReference the reference to obtain the source type from
* @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference} * @return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}
* @since 4.3.12 * @since 4.3.12
* @see #forType(Type) * @see #forType(Type)
*/ */
@@ -1403,23 +1419,23 @@ public class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Type} backed by a given * Return a {@code ResolvableType} for the specified {@link Type} backed by a given
* {@link VariableResolver}. * {@link VariableResolver}.
* @param type the source type or {@code null} * @param type the source type or {@code null}
* @param variableResolver the variable resolver or {@code null} * @param variableResolver the variable resolver or {@code null}
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} * @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver}
*/ */
static ResolvableType forType(@Nullable Type type, @Nullable VariableResolver variableResolver) { static ResolvableType forType(@Nullable Type type, @Nullable VariableResolver variableResolver) {
return forType(type, null, variableResolver); return forType(type, null, variableResolver);
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Type} backed by a given * Return a {@code ResolvableType} for the specified {@link Type} backed by a given
* {@link VariableResolver}. * {@link VariableResolver}.
* @param type the source type or {@code null} * @param type the source type or {@code null}
* @param typeProvider the type provider or {@code null} * @param typeProvider the type provider or {@code null}
* @param variableResolver the variable resolver or {@code null} * @param variableResolver the variable resolver or {@code null}
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} * @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver}
*/ */
static ResolvableType forType( static ResolvableType forType(
@Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { @Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
@@ -1434,7 +1450,7 @@ public class ResolvableType implements Serializable {
// For simple Class references, build the wrapper right away - // For simple Class references, build the wrapper right away -
// no expensive resolution necessary, so not worth caching... // no expensive resolution necessary, so not worth caching...
if (type instanceof Class) { if (type instanceof Class) {
return new ResolvableType(type, typeProvider, variableResolver, (ResolvableType) null); return new ResolvableType(type, null, typeProvider, variableResolver);
} }
// Purge empty entries on access since we don't have a clean-up thread or the like. // Purge empty entries on access since we don't have a clean-up thread or the like.

View File

@@ -123,6 +123,7 @@ class ResolvableTypeTests {
assertThat(type.getRawClass()).isEqualTo(ExtendsList.class); assertThat(type.getRawClass()).isEqualTo(ExtendsList.class);
assertThat(type.isAssignableFrom(ExtendsList.class)).isTrue(); assertThat(type.isAssignableFrom(ExtendsList.class)).isTrue();
assertThat(type.isAssignableFrom(ArrayList.class)).isFalse(); assertThat(type.isAssignableFrom(ArrayList.class)).isFalse();
assertThat(type).isNotEqualTo(ResolvableType.forClass(ExtendsList.class));
} }
@Test @Test
@@ -132,6 +133,7 @@ class ResolvableTypeTests {
assertThat(type.getRawClass()).isEqualTo(Object.class); assertThat(type.getRawClass()).isEqualTo(Object.class);
assertThat(type.isAssignableFrom(Object.class)).isTrue(); assertThat(type.isAssignableFrom(Object.class)).isTrue();
assertThat(type.isAssignableFrom(String.class)).isTrue(); assertThat(type.isAssignableFrom(String.class)).isTrue();
assertThat(type).isNotEqualTo(ResolvableType.forClass(null));
} }
@Test // gh-23321 @Test // gh-23321
@@ -200,8 +202,8 @@ class ResolvableTypeTests {
@Test @Test
void forFieldMustNotBeNull() throws Exception { void forFieldMustNotBeNull() throws Exception {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forField(null)) .isThrownBy(() -> ResolvableType.forField(null))
.withMessage("Field must not be null"); .withMessage("Field must not be null");
} }
@Test @Test
@@ -214,8 +216,8 @@ class ResolvableTypeTests {
@Test @Test
void forConstructorParameterMustNotBeNull() throws Exception { void forConstructorParameterMustNotBeNull() throws Exception {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0)) .isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0))
.withMessage("Constructor must not be null"); .withMessage("Constructor must not be null");
} }
@Test @Test
@@ -228,8 +230,8 @@ class ResolvableTypeTests {
@Test @Test
void forMethodParameterByIndexMustNotBeNull() throws Exception { void forMethodParameterByIndexMustNotBeNull() throws Exception {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forMethodParameter(null, 0)) .isThrownBy(() -> ResolvableType.forMethodParameter(null, 0))
.withMessage("Method must not be null"); .withMessage("Method must not be null");
} }
@Test @Test
@@ -268,8 +270,8 @@ class ResolvableTypeTests {
@Test @Test
void forMethodParameterMustNotBeNull() throws Exception { void forMethodParameterMustNotBeNull() throws Exception {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forMethodParameter(null)) .isThrownBy(() -> ResolvableType.forMethodParameter(null))
.withMessage("MethodParameter must not be null"); .withMessage("MethodParameter must not be null");
} }
@Test // SPR-16210 @Test // SPR-16210
@@ -295,8 +297,23 @@ class ResolvableTypeTests {
@Test @Test
void forMethodReturnMustNotBeNull() throws Exception { void forMethodReturnMustNotBeNull() throws Exception {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forMethodReturnType(null)) .isThrownBy(() -> ResolvableType.forMethodReturnType(null))
.withMessage("Method must not be null"); .withMessage("Method must not be null");
}
@Test // gh-27748
void genericMatchesReturnType() throws Exception {
Method method = SomeRepository.class.getMethod("someMethod", Class.class, Class.class, Class.class);
ResolvableType returnType = ResolvableType.forMethodReturnType(method, SomeRepository.class);
ResolvableType arg0 = ResolvableType.forMethodParameter(method, 0, SomeRepository.class); // generic[0]=T
ResolvableType arg1 = ResolvableType.forMethodParameter(method, 1, SomeRepository.class); // generic[0]=?
ResolvableType arg2 = ResolvableType.forMethodParameter(method, 2, SomeRepository.class); // generic[0]=java.lang.Object
assertThat(returnType.equalsType(arg0.as(Class.class).getGeneric(0))).isTrue();
assertThat(returnType.equalsType(arg1.as(Class.class).getGeneric(0))).isFalse();
assertThat(returnType.equalsType(arg2.as(Class.class).getGeneric(0))).isFalse();
} }
@Test @Test
@@ -964,8 +981,8 @@ class ResolvableTypeTests {
@Test @Test
void isAssignableFromMustNotBeNull() throws Exception { void isAssignableFromMustNotBeNull() throws Exception {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null)) .isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null))
.withMessage("ResolvableType must not be null"); .withMessage("ResolvableType must not be null");
} }
@Test @Test
@@ -1220,9 +1237,9 @@ class ResolvableTypeTests {
@Test @Test
void forClassWithMismatchedGenerics() throws Exception { void forClassWithMismatchedGenerics() throws Exception {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class)) .isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class))
.withMessageContaining("Mismatched number of generics specified for") .withMessageContaining("Mismatched number of generics specified for")
.withMessageContaining("java.util.Map<K,V>"); .withMessageContaining("java.util.Map<K,V>");
} }
@Test @Test
@@ -1368,6 +1385,12 @@ class ResolvableTypeTests {
} }
interface SomeRepository {
<T> T someMethod(Class<T> arg0, Class<?> arg1, Class<Object> arg2);
}
static class Fields<T> { static class Fields<T> {
public List classType; public List classType;