diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 8316e4a4ef..a5150cff17 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -100,6 +100,12 @@ public class ResolvableType implements Serializable { */ 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. */ @@ -112,12 +118,6 @@ public class ResolvableType implements Serializable { @Nullable 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 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. */ private ResolvableType( Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { this.type = type; + this.componentType = null; this.typeProvider = typeProvider; this.variableResolver = variableResolver; - this.componentType = null; this.hash = calculateHashCode(); 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. * @since 4.2 */ @@ -161,39 +161,39 @@ public class ResolvableType implements Serializable { @Nullable VariableResolver variableResolver, @Nullable Integer hash) { this.type = type; + this.componentType = null; this.typeProvider = typeProvider; this.variableResolver = variableResolver; - this.componentType = null; this.hash = hash; 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. */ - private ResolvableType(Type type, @Nullable TypeProvider typeProvider, - @Nullable VariableResolver variableResolver, @Nullable ResolvableType componentType) { + private ResolvableType(Type type, @Nullable ResolvableType componentType, + @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { this.type = type; + this.componentType = componentType; this.typeProvider = typeProvider; this.variableResolver = variableResolver; - this.componentType = componentType; this.hash = null; 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. *

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.componentType = null; this.typeProvider = null; this.variableResolver = null; - this.componentType = 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}, - * {@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 * 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} * 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 + * @return a {@code ResolvableType} representing this object as the specified * type, or {@link #NONE} if not resolvable as that type * @see #asCollection() * @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. *

If no supertype is available this method returns {@link #NONE}. - *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. + *

Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. * @see #getInterfaces() */ 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 * empty array is returned. - *

Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}. + *

Note: The resulting {@code ResolvableType} instances may not be {@link Serializable}. * @see #getSuperType() */ 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. *

See {@link #getNested(int, Map)} for details. * @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) { return getNested(nestingLevel, null); } /** - * Return a {@link ResolvableType} for the specified nesting level. + * Return a {@code 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 {@code List>} 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 * @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} + * @return a {@code ResolvableType} for the nested level, or {@link #NONE} */ public ResolvableType getNested(int nestingLevel, @Nullable Map typeIndexesPerLevel) { 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 * {@code Map>}, {@code getGeneric(0)} will access the * {@code Integer}. Nested generics can be accessed by specifying multiple indexes; @@ -677,7 +677,7 @@ public class ResolvableType implements Serializable { *

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} + * @return a {@code ResolvableType} for the specified generic, or {@link #NONE} * @see #hasGenerics() * @see #getGenerics() * @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}. - *

Note: The returned {@link ResolvableType} should only be used as an intermediary + *

Note: The returned {@code ResolvableType} should only be used as an intermediary * as it cannot be serialized. */ 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 public boolean equals(@Nullable Object other) { if (this == other) { return true; } - if (!(other instanceof ResolvableType otherType)) { + if (other == null || other.getClass() != getClass()) { return false; } + ResolvableType otherType = (ResolvableType) other; - if (!ObjectUtils.nullSafeEquals(this.type, otherType.type)) { + if (!equalsType(otherType)) { return false; } if (this.typeProvider != otherType.typeProvider && @@ -932,12 +938,22 @@ public class ResolvableType implements Serializable { !ObjectUtils.nullSafeEquals(this.variableResolver.getSource(), otherType.variableResolver.getSource()))) { return false; } - if (!ObjectUtils.nullSafeEquals(this.componentType, otherType.componentType)) { - return false; - } return true; } + /** + * Check for type-level equality with another {@code ResolvableType}. + *

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 public int hashCode() { return (this.hash != null ? this.hash : calculateHashCode()); @@ -945,20 +961,20 @@ public class ResolvableType implements Serializable { private int calculateHashCode() { int hashCode = ObjectUtils.nullSafeHashCode(this.type); + if (this.componentType != null) { + hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.componentType); + } 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 {@link ResolvableType} to a {@link VariableResolver}. + * Adapts this {@code ResolvableType} to a {@link VariableResolver}. */ @Nullable VariableResolver asVariableResolver() { @@ -1004,12 +1020,12 @@ public class ResolvableType implements Serializable { // 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. *

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 + * @return a {@code ResolvableType} for the specified class * @see #forClass(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 * {@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 + * @return a {@code ResolvableType} for the specified class * @since 4.2 * @see #forClass(Class) * @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. *

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 + * @return a {@code ResolvableType} for the specified base type backed by the * given implementation class * @see #forClass(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 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...) */ 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 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...) */ 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 - * 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}. * @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} * @since 4.2 * @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 - * @return a {@link ResolvableType} for the specified field + * @return a {@code ResolvableType} for the specified field * @see #forField(Field, Class) */ 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. *

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 + * @return a {@code ResolvableType} for the specified field * @see #forField(Field) */ 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. *

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 + * @return a {@code ResolvableType} for the specified field * @see #forField(Field) */ 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. * @param field the source field * @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. *

Use this variant when the class that declares the field includes generic * 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 * generic type; etc) * @param implementationClass the implementation class - * @return a {@link ResolvableType} for the specified field + * @return a {@code ResolvableType} for the specified field * @see #forField(Field) */ 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 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) */ 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 * 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 + * @return a {@code ResolvableType} for the specified constructor parameter * @see #forConstructorParameter(Constructor, int) */ 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 - * @return a {@link ResolvableType} for the specified method return + * @return a {@code ResolvableType} for the specified method return * @see #forMethodReturnType(Method, Class) */ 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. *

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 + * @return a {@code ResolvableType} for the specified method return * @see #forMethodReturnType(Method) */ 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 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(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 * 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 + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int, Class) * @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}) - * @return a {@link ResolvableType} for the specified method parameter + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int) */ 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 * 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 + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(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. * @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 + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int) */ 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 * 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) * @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 * @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 - * @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) { 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 new ResolvableType(arrayClass, componentType, null, null); } /** - * Return a {@link ResolvableType} for the specified {@link Type}. - *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. + * Return a {@code ResolvableType} for the specified {@link Type}. + *

Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. * @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) */ 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. - *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. + *

Note: The resulting {@code 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 + * @return a {@code ResolvableType} for the specified {@link Type} and owner * @see #forType(Type) */ 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}. - *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. + * Return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}. + *

Note: The resulting {@code 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} + * @return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference} * @since 4.3.12 * @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}. * @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} + * @return a {@code 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 {@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}. * @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} + * @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver} */ static ResolvableType forType( @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 - // no expensive resolution necessary, so not worth caching... 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. diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index db7bb45676..f1a439706a 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -123,6 +123,7 @@ class ResolvableTypeTests { assertThat(type.getRawClass()).isEqualTo(ExtendsList.class); assertThat(type.isAssignableFrom(ExtendsList.class)).isTrue(); assertThat(type.isAssignableFrom(ArrayList.class)).isFalse(); + assertThat(type).isNotEqualTo(ResolvableType.forClass(ExtendsList.class)); } @Test @@ -132,6 +133,7 @@ class ResolvableTypeTests { assertThat(type.getRawClass()).isEqualTo(Object.class); assertThat(type.isAssignableFrom(Object.class)).isTrue(); assertThat(type.isAssignableFrom(String.class)).isTrue(); + assertThat(type).isNotEqualTo(ResolvableType.forClass(null)); } @Test // gh-23321 @@ -200,8 +202,8 @@ class ResolvableTypeTests { @Test void forFieldMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forField(null)) - .withMessage("Field must not be null"); + .isThrownBy(() -> ResolvableType.forField(null)) + .withMessage("Field must not be null"); } @Test @@ -214,8 +216,8 @@ class ResolvableTypeTests { @Test void forConstructorParameterMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0)) - .withMessage("Constructor must not be null"); + .isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0)) + .withMessage("Constructor must not be null"); } @Test @@ -228,8 +230,8 @@ class ResolvableTypeTests { @Test void forMethodParameterByIndexMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forMethodParameter(null, 0)) - .withMessage("Method must not be null"); + .isThrownBy(() -> ResolvableType.forMethodParameter(null, 0)) + .withMessage("Method must not be null"); } @Test @@ -268,8 +270,8 @@ class ResolvableTypeTests { @Test void forMethodParameterMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forMethodParameter(null)) - .withMessage("MethodParameter must not be null"); + .isThrownBy(() -> ResolvableType.forMethodParameter(null)) + .withMessage("MethodParameter must not be null"); } @Test // SPR-16210 @@ -295,8 +297,23 @@ class ResolvableTypeTests { @Test void forMethodReturnMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forMethodReturnType(null)) - .withMessage("Method must not be null"); + .isThrownBy(() -> ResolvableType.forMethodReturnType(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 @@ -964,8 +981,8 @@ class ResolvableTypeTests { @Test void isAssignableFromMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null)) - .withMessage("ResolvableType must not be null"); + .isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null)) + .withMessage("ResolvableType must not be null"); } @Test @@ -1220,9 +1237,9 @@ class ResolvableTypeTests { @Test void forClassWithMismatchedGenerics() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class)) - .withMessageContaining("Mismatched number of generics specified for") - .withMessageContaining("java.util.Map"); + .isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class)) + .withMessageContaining("Mismatched number of generics specified for") + .withMessageContaining("java.util.Map"); } @Test @@ -1368,6 +1385,12 @@ class ResolvableTypeTests { } + interface SomeRepository { + + T someMethod(Class arg0, Class arg1, Class arg2); + } + + static class Fields { public List classType;