Polishing (includes nullable declaration for internal type array)

This commit is contained in:
Juergen Hoeller
2026-03-23 20:11:54 +01:00
parent 4b211643bb
commit f0472ea1c0
@@ -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 {
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 {
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 {
Type[] lhsTypeArguments = lhsType.getActualTypeArguments();
Type[] rhsTypeArguments = rhsType.getActualTypeArguments();
if (lhsTypeArguments.length != rhsTypeArguments.length) {
return false;
}
@@ -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 {
}
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 {
return false;
}
}
for (Type rBound : rLowerBounds) {
if (!isAssignableBound(lBound, rBound)) {
return false;
@@ -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 {
return false;
}
}
for (Type lBound : lLowerBounds) {
if (!isAssignableBound(rhsType, lBound)) {
return false;
@@ -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);
}