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 6ddb216edc..69692283c6 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -122,6 +122,17 @@ public final class ResolvableType implements Serializable { private ResolvableType[] generics; + /** + * Private constructor used to create a new {@link ResolvableType} for cache key purposes. + */ + private ResolvableType(Type type, TypeProvider typeProvider, VariableResolver variableResolver) { + this.type = type; + this.typeProvider = typeProvider; + this.variableResolver = variableResolver; + this.componentType = null; + this.resolved = null; + } + /** * Private constructor used to create a new {@link ResolvableType} for resolution purposes. */ @@ -135,17 +146,6 @@ public final class ResolvableType implements Serializable { this.resolved = resolveClass(); } - /** - * Private constructor used to create a new {@link ResolvableType} for cache key purposes. - */ - private ResolvableType(Type type, TypeProvider typeProvider, VariableResolver variableResolver) { - this.type = type; - this.typeProvider = typeProvider; - this.variableResolver = variableResolver; - this.componentType = null; - this.resolved = null; - } - /** * Return the underling Java {@link Type} being managed. With the exception of @@ -598,7 +598,7 @@ public final class ResolvableType implements Serializable { return EMPTY_TYPES_ARRAY; } if (this.generics == null) { - if (this.type instanceof Class) { + if (this.type instanceof Class) { Class typeClass = (Class) this.type; this.generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver); } @@ -691,7 +691,7 @@ public final class ResolvableType implements Serializable { } private Class resolveClass() { - if (this.type instanceof Class || this.type == null) { + if (this.type instanceof Class || this.type == null) { return (Class) this.type; } if (this.type instanceof GenericArrayType) { @@ -762,6 +762,66 @@ public final class ResolvableType implements Serializable { return null; } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof ResolvableType)) { + return false; + } + ResolvableType otherType = (ResolvableType) other; + return (ObjectUtils.nullSafeEquals(this.type, otherType.type) && + ObjectUtils.nullSafeEquals(getSource(), otherType.getSource()) && + variableResolverSourceEquals(otherType.variableResolver) && + ObjectUtils.nullSafeEquals(this.componentType, otherType.componentType)); + } + + @Override + public int hashCode() { + int hashCode = ObjectUtils.nullSafeHashCode(this.type); + hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(getSource()); + hashCode = 31 * hashCode + variableResolverSourceHashCode(); + hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.componentType); + return hashCode; + } + + private boolean variableResolverSourceEquals(VariableResolver other) { + if (this.variableResolver == null) { + return (other == null); + } + if (other == null) { + return false; + } + return ObjectUtils.nullSafeEquals(this.variableResolver.getSource(), other.getSource()); + } + + private int variableResolverSourceHashCode() { + int hashCode = 0; + if (this.variableResolver != null) { + hashCode = ObjectUtils.nullSafeHashCode(this.variableResolver.getSource()); + } + return hashCode; + } + + /** + * Adapts this {@link ResolvableType} to a {@link VariableResolver}. + */ + VariableResolver asVariableResolver() { + if (this == NONE) { + return null; + } + return new DefaultVariableResolver(); + } + + /** + * Custom serialization support for {@link #NONE}. + */ + private Object readResolve() throws ObjectStreamException { + return (this.type == null ? NONE : this); + } + /** * Return a String representation of this type in its fully resolved form * (including any generic parameters). @@ -791,76 +851,13 @@ public final class ResolvableType implements Serializable { return result.toString(); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ResolvableType)) { - return false; - } - ResolvableType other = (ResolvableType) obj; - return (ObjectUtils.nullSafeEquals(this.type, other.type) && - ObjectUtils.nullSafeEquals(getSource(), other.getSource()) && - variableResolverSourceEquals(other.variableResolver) && - ObjectUtils.nullSafeEquals(this.componentType, other.componentType)); - } - @Override - public int hashCode() { - int hashCode = ObjectUtils.nullSafeHashCode(this.type); - hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(getSource()); - hashCode = 31 * hashCode + variableResolverSourceHashCode(); - hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.componentType); - return hashCode; - } + // Factory methods /** - * Custom serialization support for {@link #NONE}. - */ - private Object readResolve() throws ObjectStreamException { - return (this.type == null ? NONE : this); - } - - /** - * Adapts this {@link ResolvableType} to a {@link VariableResolver}. - */ - VariableResolver asVariableResolver() { - if (this == NONE) { - return null; - } - return new DefaultVariableResolver(); - } - - private boolean variableResolverSourceEquals(VariableResolver other) { - if (this.variableResolver == null) { - return (other == null); - } - if (other == null) { - return false; - } - return ObjectUtils.nullSafeEquals(this.variableResolver.getSource(), other.getSource()); - } - - private int variableResolverSourceHashCode() { - int hashCode = 0; - if (this.variableResolver != null) { - hashCode = ObjectUtils.nullSafeHashCode(this.variableResolver.getSource()); - } - return hashCode; - } - - private static ResolvableType[] forTypes(Type[] types, VariableResolver owner) { - ResolvableType[] result = new ResolvableType[types.length]; - for (int i = 0; i < types.length; i++) { - result[i] = forType(types[i], owner); - } - return result; - } - - /** - * Return a {@link ResolvableType} for the specified {@link Class}. For example - * {@code ResolvableType.forClass(MyArrayList.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 sourceClass the source class (must not be {@code null} * @return a {@link ResolvableType} for the specified class * @see #forClass(Class, Class) @@ -872,9 +869,9 @@ public final class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Class} with a given - * implementation. For example - * {@code ResolvableType.forClass(List.class, MyArrayList.class)}. + * Return a {@link ResolvableType} for the specified {@link Class} + * with a given implementation. + * For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}. * @param sourceClass the source class (must not be {@code null} * @param implementationClass the implementation class * @return a {@link ResolvableType} for the specified class backed by the given @@ -888,6 +885,38 @@ public final class ResolvableType implements Serializable { return (asType == NONE ? forType(sourceClass) : asType); } + /** + * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. + * @param sourceClass the source class + * @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 sourceClass, Class... generics) { + Assert.notNull(sourceClass, "Source class must not be null"); + Assert.notNull(generics, "Generics 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(sourceClass, resolvableGenerics); + } + + /** + * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. + * @param sourceClass the source class + * @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 sourceClass, ResolvableType... generics) { + Assert.notNull(sourceClass, "Source class must not be null"); + Assert.notNull(generics, "Generics must not be null"); + TypeVariable[] variables = sourceClass.getTypeParameters(); + Assert.isTrue(variables.length == generics.length, "Mismatched number of generics specified"); + return forType(sourceClass, new TypeVariablesVariableResolver(variables, generics)); + } + /** * Return a {@link ResolvableType} for the specified {@link Field}. * @param field the source field @@ -1102,41 +1131,17 @@ public final class ResolvableType implements Serializable { * @return a {@link ResolvableType} as an array of the specified component type */ public static ResolvableType forArrayComponent(ResolvableType componentType) { - Assert.notNull(componentType, "ComponentType must not be null"); + Assert.notNull(componentType, "componentType must not be null"); Class arrayClass = Array.newInstance(componentType.resolve(), 0).getClass(); return new ResolvableType(arrayClass, null, null, componentType); } - /** - * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. - * @param sourceClass the source class - * @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 sourceClass, Class... generics) { - Assert.notNull(sourceClass, "Source class must not be null"); - Assert.notNull(generics, "Generics must not be null"); - ResolvableType[] resolvableGenerics = new ResolvableType[generics.length]; - for (int i = 0; i < generics.length; i++) { - resolvableGenerics[i] = forClass(generics[i]); + private static ResolvableType[] forTypes(Type[] types, VariableResolver owner) { + ResolvableType[] result = new ResolvableType[types.length]; + for (int i = 0; i < types.length; i++) { + result[i] = forType(types[i], owner); } - return forClassWithGenerics(sourceClass, resolvableGenerics); - } - - /** - * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. - * @param sourceClass the source class - * @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 sourceClass, ResolvableType... generics) { - Assert.notNull(sourceClass, "Source class must not be null"); - Assert.notNull(generics, "Generics must not be null"); - TypeVariable[] variables = sourceClass.getTypeParameters(); - Assert.isTrue(variables.length == generics.length, "Mismatched number of generics specified"); - return forType(sourceClass, new TypeVariablesVariableResolver(variables, generics)); + return result; } /** @@ -1198,7 +1203,7 @@ public final 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) { + if (type instanceof Class) { return new ResolvableType(type, typeProvider, variableResolver, null); }