|
|
|
|
@ -38,15 +38,16 @@ import org.springframework.lang.Contract;
@@ -38,15 +38,16 @@ import org.springframework.lang.Contract;
|
|
|
|
|
*/ |
|
|
|
|
public abstract class TypeUtils { |
|
|
|
|
|
|
|
|
|
private static final Type[] IMPLICIT_LOWER_BOUNDS = { null }; |
|
|
|
|
private static final @Nullable Type[] IMPLICIT_LOWER_BOUNDS = { null }; |
|
|
|
|
|
|
|
|
|
private static final Type[] IMPLICIT_UPPER_BOUNDS = { Object.class }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Check if the right-hand side type may be assigned to the left-hand side |
|
|
|
|
* type following the Java generics rules. |
|
|
|
|
* @param lhsType the target type (left-hand side (LHS) type) |
|
|
|
|
* @param rhsType the value type (right-hand side (RHS) type) that should |
|
|
|
|
* @param lhsType the target type (left-hand side type) |
|
|
|
|
* @param rhsType the value type (right-hand side type) that should |
|
|
|
|
* be assigned to the target type |
|
|
|
|
* @return {@code true} if {@code rhsType} is assignable to {@code lhsType} |
|
|
|
|
* @see ClassUtils#isAssignable(Class, Class) |
|
|
|
|
@ -55,37 +56,34 @@ public abstract class TypeUtils {
@@ -55,37 +56,34 @@ public abstract class TypeUtils {
|
|
|
|
|
Assert.notNull(lhsType, "Left-hand side type must not be null"); |
|
|
|
|
Assert.notNull(rhsType, "Right-hand side type must not be null"); |
|
|
|
|
|
|
|
|
|
// all types are assignable to themselves and to class Object
|
|
|
|
|
// All types are assignable to themselves and to class Object.
|
|
|
|
|
if (lhsType.equals(rhsType) || Object.class == lhsType) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (lhsType instanceof Class<?> lhsClass) { |
|
|
|
|
// just comparing two classes
|
|
|
|
|
// Just comparing two classes...
|
|
|
|
|
if (rhsType instanceof Class<?> rhsClass) { |
|
|
|
|
return ClassUtils.isAssignable(lhsClass, rhsClass); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (rhsType instanceof ParameterizedType rhsParameterizedType) { |
|
|
|
|
Type rhsRaw = rhsParameterizedType.getRawType(); |
|
|
|
|
|
|
|
|
|
// a parameterized type is always assignable to its raw class type
|
|
|
|
|
// A parameterized type is always assignable to its raw class type.
|
|
|
|
|
if (rhsRaw instanceof Class<?> rhRawClass) { |
|
|
|
|
return ClassUtils.isAssignable(lhsClass, rhRawClass); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (lhsClass.isArray() && rhsType instanceof GenericArrayType rhsGenericArrayType) { |
|
|
|
|
Type rhsComponent = rhsGenericArrayType.getGenericComponentType(); |
|
|
|
|
|
|
|
|
|
return isAssignable(lhsClass.componentType(), rhsComponent); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// parameterized types are only assignable to other parameterized types and class types
|
|
|
|
|
// Parameterized types are only assignable to other parameterized types and class types.
|
|
|
|
|
if (lhsType instanceof ParameterizedType lhsParameterizedType) { |
|
|
|
|
if (rhsType instanceof Class<?> rhsClass) { |
|
|
|
|
Type lhsRaw = lhsParameterizedType.getRawType(); |
|
|
|
|
|
|
|
|
|
if (lhsRaw instanceof Class<?> lhsClass) { |
|
|
|
|
return ClassUtils.isAssignable(lhsClass, rhsClass); |
|
|
|
|
} |
|
|
|
|
@ -97,7 +95,6 @@ public abstract class TypeUtils {
@@ -97,7 +95,6 @@ public abstract class TypeUtils {
|
|
|
|
|
|
|
|
|
|
if (lhsType instanceof GenericArrayType lhsGenericArrayType) { |
|
|
|
|
Type lhsComponent = lhsGenericArrayType.getGenericComponentType(); |
|
|
|
|
|
|
|
|
|
if (rhsType instanceof Class<?> rhsClass && rhsClass.isArray()) { |
|
|
|
|
return isAssignable(lhsComponent, rhsClass.componentType()); |
|
|
|
|
} |
|
|
|
|
@ -122,7 +119,6 @@ public abstract class TypeUtils {
@@ -122,7 +119,6 @@ public abstract class TypeUtils {
|
|
|
|
|
|
|
|
|
|
Type[] lhsTypeArguments = lhsType.getActualTypeArguments(); |
|
|
|
|
Type[] rhsTypeArguments = rhsType.getActualTypeArguments(); |
|
|
|
|
|
|
|
|
|
if (lhsTypeArguments.length != rhsTypeArguments.length) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
@ -130,7 +126,6 @@ public abstract class TypeUtils {
@@ -130,7 +126,6 @@ public abstract class TypeUtils {
|
|
|
|
|
for (int size = lhsTypeArguments.length, i = 0; i < size; ++i) { |
|
|
|
|
Type lhsArg = lhsTypeArguments[i]; |
|
|
|
|
Type rhsArg = rhsTypeArguments[i]; |
|
|
|
|
|
|
|
|
|
if (!lhsArg.equals(rhsArg) && |
|
|
|
|
!(lhsArg instanceof WildcardType wildcardType && isAssignable(wildcardType, rhsArg))) { |
|
|
|
|
return false; |
|
|
|
|
@ -141,17 +136,14 @@ public abstract class TypeUtils {
@@ -141,17 +136,14 @@ public abstract class TypeUtils {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static boolean isAssignable(WildcardType lhsType, Type rhsType) { |
|
|
|
|
Type[] lUpperBounds = getUpperBounds(lhsType); |
|
|
|
|
|
|
|
|
|
Type[] lLowerBounds = getLowerBounds(lhsType); |
|
|
|
|
@Nullable Type[] lUpperBounds = getUpperBounds(lhsType); |
|
|
|
|
@Nullable Type[] lLowerBounds = getLowerBounds(lhsType); |
|
|
|
|
|
|
|
|
|
if (rhsType instanceof WildcardType rhsWcType) { |
|
|
|
|
// both the upper and lower bounds of the right-hand side must be
|
|
|
|
|
// completely enclosed in the upper and lower bounds of the left-
|
|
|
|
|
// hand side.
|
|
|
|
|
Type[] rUpperBounds = getUpperBounds(rhsWcType); |
|
|
|
|
|
|
|
|
|
Type[] rLowerBounds = getLowerBounds(rhsWcType); |
|
|
|
|
// Both the upper and lower bounds of the right-hand side must be
|
|
|
|
|
// completely enclosed in the upper and lower bounds of the left-hand side.
|
|
|
|
|
@Nullable Type[] rUpperBounds = getUpperBounds(rhsWcType); |
|
|
|
|
@Nullable Type[] rLowerBounds = getLowerBounds(rhsWcType); |
|
|
|
|
|
|
|
|
|
for (Type lBound : lUpperBounds) { |
|
|
|
|
for (Type rBound : rUpperBounds) { |
|
|
|
|
@ -159,7 +151,6 @@ public abstract class TypeUtils {
@@ -159,7 +151,6 @@ public abstract class TypeUtils {
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (Type rBound : rLowerBounds) { |
|
|
|
|
if (!isAssignableBound(lBound, rBound)) { |
|
|
|
|
return false; |
|
|
|
|
@ -173,7 +164,6 @@ public abstract class TypeUtils {
@@ -173,7 +164,6 @@ public abstract class TypeUtils {
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (Type rBound : rLowerBounds) { |
|
|
|
|
if (!isAssignableBound(rBound, lBound)) { |
|
|
|
|
return false; |
|
|
|
|
@ -187,7 +177,6 @@ public abstract class TypeUtils {
@@ -187,7 +177,6 @@ public abstract class TypeUtils {
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (Type lBound : lLowerBounds) { |
|
|
|
|
if (!isAssignableBound(rhsType, lBound)) { |
|
|
|
|
return false; |
|
|
|
|
@ -198,17 +187,15 @@ public abstract class TypeUtils {
@@ -198,17 +187,15 @@ public abstract class TypeUtils {
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static Type[] getLowerBounds(WildcardType wildcardType) { |
|
|
|
|
private static @Nullable Type[] getLowerBounds(WildcardType wildcardType) { |
|
|
|
|
Type[] lowerBounds = wildcardType.getLowerBounds(); |
|
|
|
|
|
|
|
|
|
// supply the implicit lower bound if none are specified
|
|
|
|
|
// Supply the implicit lower bound if none are specified.
|
|
|
|
|
return (lowerBounds.length == 0 ? IMPLICIT_LOWER_BOUNDS : lowerBounds); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static Type[] getUpperBounds(WildcardType wildcardType) { |
|
|
|
|
Type[] upperBounds = wildcardType.getUpperBounds(); |
|
|
|
|
|
|
|
|
|
// supply the implicit upper bound if none are specified
|
|
|
|
|
// Supply the implicit upper bound if none are specified.
|
|
|
|
|
return (upperBounds.length == 0 ? IMPLICIT_UPPER_BOUNDS : upperBounds); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|