diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 7adc7059ca6..62c4867ae28 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -279,13 +279,30 @@ public class TypeDescriptor { } /** - * Returns true if an object of this type can be assigned to a reference of the given type. - * @param typeDescriptor the descriptor for the target type + * Returns true if an object of this type descriptor can be assigned to the location described by the given type descriptor. + * For example, valueOf(String.class).isAssignableTo(valueOf(CharSequence.class)) returns true because a String value can be assigned to a CharSequence variable. + * On the other hand, valueOf(Number.class).isAssignableTo(valueOf(Integer.class)) returns false because, while all Integers are Numbers, not all Numbers are Integers. + *

+ * For arrays, collections, and maps, element and key/value types are checked if declared. + * For example, a List<String> field value is assignable to a Collection<CharSequence> field, but List<Number> is not assignable to List<Integer>. * @return true if this type is assignable to the type represented by the provided type descriptor. * @see #getObjectType() */ public boolean isAssignableTo(TypeDescriptor typeDescriptor) { - return typeDescriptor.getObjectType().isAssignableFrom(getObjectType()); + boolean typesAssignable = typeDescriptor.getObjectType().isAssignableFrom(getObjectType()); + if (!typesAssignable) { + return false; + } + if (isArray()) { + return getElementTypeDescriptor().isAssignableTo(typeDescriptor.getElementTypeDescriptor()); + } else if (isCollection()) { + return isNestedAssignable(getElementTypeDescriptor(), typeDescriptor.getElementTypeDescriptor()); + } else if (isMap()) { + return isNestedAssignable(getMapKeyTypeDescriptor(), typeDescriptor.getMapKeyTypeDescriptor()) && + isNestedAssignable(getMapValueTypeDescriptor(), typeDescriptor.getMapValueTypeDescriptor()); + } else { + return true; + } } // indexable type descriptor operations @@ -534,7 +551,14 @@ public class TypeDescriptor { return value != null ? new TypeDescriptor(value.getClass(), null, null, null, annotations) : null; } } - + + private boolean isNestedAssignable(TypeDescriptor nestedTypeDescriptor, TypeDescriptor otherNestedTypeDescriptor) { + if (nestedTypeDescriptor == null || otherNestedTypeDescriptor == null) { + return true; + } + return nestedTypeDescriptor.isAssignableTo(otherNestedTypeDescriptor); + } + private String wildcard(TypeDescriptor typeDescriptor) { return typeDescriptor != null ? typeDescriptor.toString() : "?"; } diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java index 27db5e60827..e70c6456444 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java @@ -759,7 +759,7 @@ public class TypeDescriptorTests { @Test public void isAssignableElementTypes() throws Exception { assertTrue(new TypeDescriptor(getClass().getField("listField")).isAssignableTo(new TypeDescriptor(getClass().getField("listField")))); - assertTrue(new TypeDescriptor(getClass().getField("isAssignableElementTypes")).isAssignableTo(new TypeDescriptor(getClass().getField("listField")))); + assertFalse(new TypeDescriptor(getClass().getField("isAssignableElementTypes")).isAssignableTo(new TypeDescriptor(getClass().getField("listField")))); assertTrue(TypeDescriptor.valueOf(List.class).isAssignableTo(new TypeDescriptor(getClass().getField("listField")))); } @@ -768,7 +768,7 @@ public class TypeDescriptorTests { @Test public void isAssignableMapKeyValueTypes() throws Exception { assertTrue(new TypeDescriptor(getClass().getField("mapField")).isAssignableTo(new TypeDescriptor(getClass().getField("mapField")))); - assertTrue(new TypeDescriptor(getClass().getField("isAssignableMapKeyValueTypes")).isAssignableTo(new TypeDescriptor(getClass().getField("mapField")))); + assertFalse(new TypeDescriptor(getClass().getField("isAssignableMapKeyValueTypes")).isAssignableTo(new TypeDescriptor(getClass().getField("mapField")))); assertTrue(TypeDescriptor.valueOf(Map.class).isAssignableTo(new TypeDescriptor(getClass().getField("mapField")))); }