/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.util;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;

import org.springframework.lang.Nullable;

Utility to work with Java 5 generic type parameters. Mainly for internal use within the framework.
Author:Ramnivas Laddad, Juergen Hoeller, Chris Beams
Since:2.0.7
/** * Utility to work with Java 5 generic type parameters. * Mainly for internal use within the framework. * * @author Ramnivas Laddad * @author Juergen Hoeller * @author Chris Beams * @since 2.0.7 */
public abstract class TypeUtils {
Check if the right-hand side type may be assigned to the left-hand side type following the Java generics rules.
Params:
  • lhsType – the target type
  • rhsType – the value type that should be assigned to the target type
Returns:true if rhs is assignable to lhs
/** * 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 * @param rhsType the value type that should be assigned to the target type * @return true if rhs is assignable to lhs */
public static boolean isAssignable(Type lhsType, Type rhsType) { 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 if (lhsType.equals(rhsType) || Object.class == lhsType) { return true; } if (lhsType instanceof Class) { Class<?> lhsClass = (Class<?>) lhsType; // just comparing two classes if (rhsType instanceof Class) { return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsType); } if (rhsType instanceof ParameterizedType) { Type rhsRaw = ((ParameterizedType) rhsType).getRawType(); // a parameterized type is always assignable to its raw class type if (rhsRaw instanceof Class) { return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsRaw); } } else if (lhsClass.isArray() && rhsType instanceof GenericArrayType) { Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType(); return isAssignable(lhsClass.getComponentType(), rhsComponent); } } // parameterized types are only assignable to other parameterized types and class types if (lhsType instanceof ParameterizedType) { if (rhsType instanceof Class) { Type lhsRaw = ((ParameterizedType) lhsType).getRawType(); if (lhsRaw instanceof Class) { return ClassUtils.isAssignable((Class<?>) lhsRaw, (Class<?>) rhsType); } } else if (rhsType instanceof ParameterizedType) { return isAssignable((ParameterizedType) lhsType, (ParameterizedType) rhsType); } } if (lhsType instanceof GenericArrayType) { Type lhsComponent = ((GenericArrayType) lhsType).getGenericComponentType(); if (rhsType instanceof Class) { Class<?> rhsClass = (Class<?>) rhsType; if (rhsClass.isArray()) { return isAssignable(lhsComponent, rhsClass.getComponentType()); } } else if (rhsType instanceof GenericArrayType) { Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType(); return isAssignable(lhsComponent, rhsComponent); } } if (lhsType instanceof WildcardType) { return isAssignable((WildcardType) lhsType, rhsType); } return false; } private static boolean isAssignable(ParameterizedType lhsType, ParameterizedType rhsType) { if (lhsType.equals(rhsType)) { return true; } Type[] lhsTypeArguments = lhsType.getActualTypeArguments(); Type[] rhsTypeArguments = rhsType.getActualTypeArguments(); if (lhsTypeArguments.length != rhsTypeArguments.length) { return false; } 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 && isAssignable((WildcardType) lhsArg, rhsArg))) { return false; } } return true; } private static boolean isAssignable(WildcardType lhsType, Type rhsType) { Type[] lUpperBounds = lhsType.getUpperBounds(); // supply the implicit upper bound if none are specified if (lUpperBounds.length == 0) { lUpperBounds = new Type[] { Object.class }; } Type[] lLowerBounds = lhsType.getLowerBounds(); // supply the implicit lower bound if none are specified if (lLowerBounds.length == 0) { lLowerBounds = new Type[] { null }; } if (rhsType instanceof WildcardType) { // 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. WildcardType rhsWcType = (WildcardType) rhsType; Type[] rUpperBounds = rhsWcType.getUpperBounds(); if (rUpperBounds.length == 0) { rUpperBounds = new Type[] { Object.class }; } Type[] rLowerBounds = rhsWcType.getLowerBounds(); if (rLowerBounds.length == 0) { rLowerBounds = new Type[] { null }; } for (Type lBound : lUpperBounds) { for (Type rBound : rUpperBounds) { if (!isAssignableBound(lBound, rBound)) { return false; } } for (Type rBound : rLowerBounds) { if (!isAssignableBound(lBound, rBound)) { return false; } } } for (Type lBound : lLowerBounds) { for (Type rBound : rUpperBounds) { if (!isAssignableBound(rBound, lBound)) { return false; } } for (Type rBound : rLowerBounds) { if (!isAssignableBound(rBound, lBound)) { return false; } } } } else { for (Type lBound : lUpperBounds) { if (!isAssignableBound(lBound, rhsType)) { return false; } } for (Type lBound : lLowerBounds) { if (!isAssignableBound(rhsType, lBound)) { return false; } } } return true; } public static boolean isAssignableBound(@Nullable Type lhsType, @Nullable Type rhsType) { if (rhsType == null) { return true; } if (lhsType == null) { return false; } return isAssignable(lhsType, rhsType); } }