You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/11/15 15:40:39 UTC
[commons-lang] 02/03: Sort members.
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
commit fe1c7903a8b97badb59f4c63b440c35bee4cf6c1
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Nov 15 09:37:29 2020 -0500
Sort members.
---
.../apache/commons/lang3/reflect/TypeUtils.java | 2390 ++++++++++----------
1 file changed, 1195 insertions(+), 1195 deletions(-)
diff --git a/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java b/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
index 35ad795..5fe03af 100644
--- a/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
+++ b/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
@@ -47,49 +47,6 @@ import org.apache.commons.lang3.builder.Builder;
public class TypeUtils {
/**
- * {@link WildcardType} builder.
- * @since 3.2
- */
- public static class WildcardTypeBuilder implements Builder<WildcardType> {
- /**
- * Constructor
- */
- private WildcardTypeBuilder() {
- }
-
- private Type[] upperBounds;
- private Type[] lowerBounds;
-
- /**
- * Specify upper bounds of the wildcard type to build.
- * @param bounds to set
- * @return {@code this}
- */
- public WildcardTypeBuilder withUpperBounds(final Type... bounds) {
- this.upperBounds = bounds;
- return this;
- }
-
- /**
- * Specify lower bounds of the wildcard type to build.
- * @param bounds to set
- * @return {@code this}
- */
- public WildcardTypeBuilder withLowerBounds(final Type... bounds) {
- this.lowerBounds = bounds;
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public WildcardType build() {
- return new WildcardTypeImpl(upperBounds, lowerBounds);
- }
- }
-
- /**
* GenericArrayType implementation class.
* @since 3.2
*/
@@ -108,34 +65,34 @@ public class TypeUtils {
* {@inheritDoc}
*/
@Override
- public Type getGenericComponentType() {
- return componentType;
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof GenericArrayType && TypeUtils.equals(this, (GenericArrayType) obj);
}
/**
* {@inheritDoc}
*/
@Override
- public String toString() {
- return TypeUtils.toString(this);
+ public Type getGenericComponentType() {
+ return componentType;
}
/**
* {@inheritDoc}
*/
@Override
- public boolean equals(final Object obj) {
- return obj == this || obj instanceof GenericArrayType && TypeUtils.equals(this, (GenericArrayType) obj);
+ public int hashCode() {
+ int result = 67 << 4;
+ result |= componentType.hashCode();
+ return result;
}
/**
* {@inheritDoc}
*/
@Override
- public int hashCode() {
- int result = 67 << 4;
- result |= componentType.hashCode();
- return result;
+ public String toString() {
+ return TypeUtils.toString(this);
}
}
@@ -164,16 +121,8 @@ public class TypeUtils {
* {@inheritDoc}
*/
@Override
- public Type getRawType() {
- return raw;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Type getOwnerType() {
- return useOwner;
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof ParameterizedType && TypeUtils.equals(this, ((ParameterizedType) obj));
}
/**
@@ -188,16 +137,16 @@ public class TypeUtils {
* {@inheritDoc}
*/
@Override
- public String toString() {
- return TypeUtils.toString(this);
+ public Type getOwnerType() {
+ return useOwner;
}
/**
* {@inheritDoc}
*/
@Override
- public boolean equals(final Object obj) {
- return obj == this || obj instanceof ParameterizedType && TypeUtils.equals(this, ((ParameterizedType) obj));
+ public Type getRawType() {
+ return raw;
}
/**
@@ -213,6 +162,57 @@ public class TypeUtils {
result |= Arrays.hashCode(typeArguments);
return result;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+ }
+
+ /**
+ * {@link WildcardType} builder.
+ * @since 3.2
+ */
+ public static class WildcardTypeBuilder implements Builder<WildcardType> {
+ private Type[] upperBounds;
+
+ private Type[] lowerBounds;
+ /**
+ * Constructor
+ */
+ private WildcardTypeBuilder() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public WildcardType build() {
+ return new WildcardTypeImpl(upperBounds, lowerBounds);
+ }
+
+ /**
+ * Specify lower bounds of the wildcard type to build.
+ * @param bounds to set
+ * @return {@code this}
+ */
+ public WildcardTypeBuilder withLowerBounds(final Type... bounds) {
+ this.lowerBounds = bounds;
+ return this;
+ }
+
+ /**
+ * Specify upper bounds of the wildcard type to build.
+ * @param bounds to set
+ * @return {@code this}
+ */
+ public WildcardTypeBuilder withUpperBounds(final Type... bounds) {
+ this.upperBounds = bounds;
+ return this;
+ }
}
/**
@@ -237,8 +237,8 @@ public class TypeUtils {
* {@inheritDoc}
*/
@Override
- public Type[] getUpperBounds() {
- return upperBounds.clone();
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof WildcardType && TypeUtils.equals(this, (WildcardType) obj);
}
/**
@@ -253,16 +253,8 @@ public class TypeUtils {
* {@inheritDoc}
*/
@Override
- public String toString() {
- return TypeUtils.toString(this);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(final Object obj) {
- return obj == this || obj instanceof WildcardType && TypeUtils.equals(this, (WildcardType) obj);
+ public Type[] getUpperBounds() {
+ return upperBounds.clone();
}
/**
@@ -276,6 +268,14 @@ public class TypeUtils {
result |= Arrays.hashCode(lowerBounds);
return result;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
}
/**
@@ -285,455 +285,566 @@ public class TypeUtils {
public static final WildcardType WILDCARD_ALL = wildcardType().withUpperBounds(Object.class).build();
/**
- * {@code TypeUtils} instances should NOT be constructed in standard
- * programming. Instead, the class should be used as
- * {@code TypeUtils.isAssignable(cls, toClass)}.
- * <p>
- * This constructor is public to permit tools that require a JavaBean instance
- * to operate.
- * </p>
+ * Appends {@code types} to {@code builder} with separator {@code sep}.
+ *
+ * @param builder destination
+ * @param sep separator
+ * @param types to append
+ * @return {@code builder}
+ * @since 3.2
*/
- public TypeUtils() {
- super();
+ private static <T> StringBuilder appendAllTo(final StringBuilder builder, final String sep,
+ @SuppressWarnings("unchecked") final T... types) {
+ Validate.notEmpty(Validate.noNullElements(types));
+ if (types.length > 0) {
+ builder.append(toString(types[0]));
+ for (int i = 1; i < types.length; i++) {
+ builder.append(sep).append(toString(types[i]));
+ }
+ }
+ return builder;
}
- /**
- * Tests if the subject type may be implicitly cast to the target type
- * following the Java generics rules. If both types are {@link Class}
- * objects, the method returns the result of
- * {@link ClassUtils#isAssignable(Class, Class)}.
- *
- * @param type the subject type to be assigned to the target type
- * @param toType the target type
- * @return {@code true} if {@code type} is assignable to {@code toType}.
- */
- public static boolean isAssignable(final Type type, final Type toType) {
- return isAssignable(type, toType, null);
+ private static void appendRecursiveTypes(final StringBuilder builder, final int[] recursiveTypeIndexes,
+ final Type[] argumentTypes) {
+ for (int i = 0; i < recursiveTypeIndexes.length; i++) {
+ appendAllTo(builder.append('<'), ", ", argumentTypes[i].toString()).append('>');
+ }
+
+ final Type[] argumentsFiltered = ArrayUtils.removeAll(argumentTypes, recursiveTypeIndexes);
+
+ if (argumentsFiltered.length > 0) {
+ appendAllTo(builder.append('<'), ", ", argumentsFiltered).append('>');
+ }
}
/**
- * Tests if the subject type may be implicitly cast to the target type
- * following the Java generics rules.
+ * Formats a {@link Class} as a {@link String}.
*
- * @param type the subject type to be assigned to the target type
- * @param toType the target type
- * @param typeVarAssigns optional map of type variable assignments
- * @return {@code true} if {@code type} is assignable to {@code toType}.
+ * @param cls {@code Class} to format
+ * @return String
+ * @since 3.2
*/
- private static boolean isAssignable(final Type type, final Type toType,
- final Map<TypeVariable<?>, Type> typeVarAssigns) {
- if (toType == null || toType instanceof Class<?>) {
- return isAssignable(type, (Class<?>) toType);
- }
-
- if (toType instanceof ParameterizedType) {
- return isAssignable(type, (ParameterizedType) toType, typeVarAssigns);
+ private static String classToString(final Class<?> cls) {
+ if (cls.isArray()) {
+ return toString(cls.getComponentType()) + "[]";
}
- if (toType instanceof GenericArrayType) {
- return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
- }
+ final StringBuilder buf = new StringBuilder();
- if (toType instanceof WildcardType) {
- return isAssignable(type, (WildcardType) toType, typeVarAssigns);
+ if (cls.getEnclosingClass() != null) {
+ buf.append(classToString(cls.getEnclosingClass())).append('.').append(cls.getSimpleName());
+ } else {
+ buf.append(cls.getName());
}
-
- if (toType instanceof TypeVariable<?>) {
- return isAssignable(type, (TypeVariable<?>) toType, typeVarAssigns);
+ if (cls.getTypeParameters().length > 0) {
+ buf.append('<');
+ appendAllTo(buf, ", ", cls.getTypeParameters());
+ buf.append('>');
}
-
- throw new IllegalStateException("found an unhandled type: " + toType);
+ return buf.toString();
}
/**
- * Tests if the subject type may be implicitly cast to the target class
- * following the Java generics rules.
+ * Tests, recursively, whether any of the type parameters associated with {@code type} are bound to variables.
*
- * @param type the subject type to be assigned to the target type
- * @param toClass the target class
- * @return {@code true} if {@code type} is assignable to {@code toClass}.
- */
- private static boolean isAssignable(final Type type, final Class<?> toClass) {
- if (type == null) {
- // consistency with ClassUtils.isAssignable() behavior
- return toClass == null || !toClass.isPrimitive();
- }
-
- // only a null type can be assigned to null type which
- // would have cause the previous to return true
- if (toClass == null) {
- return false;
- }
-
- // all types are assignable to themselves
- if (toClass.equals(type)) {
+ * @param type the type to check for type variables
+ * @return boolean
+ * @since 3.2
+ */
+ public static boolean containsTypeVariables(final Type type) {
+ if (type instanceof TypeVariable<?>) {
return true;
}
-
if (type instanceof Class<?>) {
- // just comparing two classes
- return ClassUtils.isAssignable((Class<?>) type, toClass);
+ return ((Class<?>) type).getTypeParameters().length > 0;
}
-
if (type instanceof ParameterizedType) {
- // only have to compare the raw type to the class
- return isAssignable(getRawType((ParameterizedType) type), toClass);
- }
-
- // *
- if (type instanceof TypeVariable<?>) {
- // if any of the bounds are assignable to the class, then the
- // type is assignable to the class.
- for (final Type bound : ((TypeVariable<?>) type).getBounds()) {
- if (isAssignable(bound, toClass)) {
+ for (final Type arg : ((ParameterizedType) type).getActualTypeArguments()) {
+ if (containsTypeVariables(arg)) {
return true;
}
}
-
return false;
}
-
- // the only classes to which a generic array type can be assigned
- // are class Object and array classes
- if (type instanceof GenericArrayType) {
- return toClass.equals(Object.class)
- || toClass.isArray()
- && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass
- .getComponentType());
- }
-
- // wildcard types are not assignable to a class (though one would think
- // "? super Object" would be assignable to Object)
if (type instanceof WildcardType) {
- return false;
+ final WildcardType wild = (WildcardType) type;
+ return containsTypeVariables(getImplicitLowerBounds(wild)[0])
+ || containsTypeVariables(getImplicitUpperBounds(wild)[0]);
}
+ return false;
+ }
- throw new IllegalStateException("found an unhandled type: " + type);
+ private static boolean containsVariableTypeSameParametrizedTypeBound(final TypeVariable<?> typeVariable,
+ final ParameterizedType parameterizedType) {
+ return ArrayUtils.contains(typeVariable.getBounds(), parameterizedType);
}
/**
- * Tests if the subject type may be implicitly cast to the target
- * parameterized type following the Java generics rules.
+ * Tries to determine the type arguments of a class/interface based on a
+ * super parameterized type's type arguments. This method is the inverse of
+ * {@link #getTypeArguments(Type, Class)} which gets a class/interface's
+ * type arguments based on a subtype. It is far more limited in determining
+ * the type arguments for the subject class's type variables in that it can
+ * only determine those parameters that map from the subject {@link Class}
+ * object to the supertype.
+ *
+ * <p>
+ * Example: {@link java.util.TreeSet
+ * TreeSet} sets its parameter as the parameter for
+ * {@link java.util.NavigableSet NavigableSet}, which in turn sets the
+ * parameter of {@link java.util.SortedSet}, which in turn sets the
+ * parameter of {@link Set}, which in turn sets the parameter of
+ * {@link java.util.Collection}, which in turn sets the parameter of
+ * {@link java.lang.Iterable}. Since {@code TreeSet}'s parameter maps
+ * (indirectly) to {@code Iterable}'s parameter, it will be able to
+ * determine that based on the super type {@code Iterable<? extends
+ * Map<Integer, ? extends Collection<?>>>}, the parameter of
+ * {@code TreeSet} is {@code ? extends Map<Integer, ? extends
+ * Collection<?>>}.
+ * </p>
*
- * @param type the subject type to be assigned to the target type
- * @param toParameterizedType the target parameterized type
- * @param typeVarAssigns a map with type variables
- * @return {@code true} if {@code type} is assignable to {@code toType}.
+ * @param cls the class whose type parameters are to be determined, not {@code null}
+ * @param superType the super type from which {@code cls}'s type
+ * arguments are to be determined, not {@code null}
+ * @return a {@code Map} of the type assignments that could be determined
+ * for the type variables in each type in the inheritance hierarchy from
+ * {@code type} to {@code toClass} inclusive.
*/
- private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType,
- final Map<TypeVariable<?>, Type> typeVarAssigns) {
- if (type == null) {
- return true;
- }
+ public static Map<TypeVariable<?>, Type> determineTypeArguments(final Class<?> cls,
+ final ParameterizedType superType) {
+ Validate.notNull(cls, "cls is null");
+ Validate.notNull(superType, "superType is null");
- // only a null type can be assigned to null type which
- // would have cause the previous to return true
- if (toParameterizedType == null) {
- return false;
- }
+ final Class<?> superClass = getRawType(superType);
- // all types are assignable to themselves
- if (toParameterizedType.equals(type)) {
- return true;
+ // compatibility check
+ if (!isAssignable(cls, superClass)) {
+ return null;
}
- // get the target type's raw type
- final Class<?> toClass = getRawType(toParameterizedType);
- // get the subject type's type arguments including owner type arguments
- // and supertype arguments up to and including the target class.
- final Map<TypeVariable<?>, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null);
-
- // null means the two types are not compatible
- if (fromTypeVarAssigns == null) {
- return false;
+ if (cls.equals(superClass)) {
+ return getTypeArguments(superType, superClass, null);
}
- // compatible types, but there's no type arguments. this is equivalent
- // to comparing Map< ?, ? > to Map, and raw types are always assignable
- // to parameterized types.
- if (fromTypeVarAssigns.isEmpty()) {
- return true;
- }
+ // get the next class in the inheritance hierarchy
+ final Type midType = getClosestParentType(cls, superClass);
- // get the target type's type arguments including owner type arguments
- final Map<TypeVariable<?>, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType,
- toClass, typeVarAssigns);
+ // can only be a class or a parameterized type
+ if (midType instanceof Class<?>) {
+ return determineTypeArguments((Class<?>) midType, superType);
+ }
- // now to check each type argument
- for (final TypeVariable<?> var : toTypeVarAssigns.keySet()) {
- final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
- final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns);
+ final ParameterizedType midParameterizedType = (ParameterizedType) midType;
+ final Class<?> midClass = getRawType(midParameterizedType);
+ // get the type variables of the mid class that map to the type
+ // arguments of the super class
+ final Map<TypeVariable<?>, Type> typeVarAssigns = determineTypeArguments(midClass, superType);
+ // map the arguments of the mid type to the class type variables
+ mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns);
- if (toTypeArg == null && fromTypeArg instanceof Class) {
- continue;
- }
+ return typeVarAssigns;
+ }
- // parameters must either be absent from the subject type, within
- // the bounds of the wildcard type, or be an exact match to the
- // parameters of the target type.
- if (fromTypeArg != null
- && !toTypeArg.equals(fromTypeArg)
- && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg,
- typeVarAssigns))) {
- return false;
- }
- }
- return true;
+ /**
+ * Tests whether {@code t} equals {@code a}.
+ *
+ * @param genericArrayType LHS
+ * @param type RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final GenericArrayType genericArrayType, final Type type) {
+ return type instanceof GenericArrayType
+ && equals(genericArrayType.getGenericComponentType(), ((GenericArrayType) type).getGenericComponentType());
}
/**
- * Look up {@code var} in {@code typeVarAssigns} <em>transitively</em>,
- * i.e. keep looking until the value found is <em>not</em> a type variable.
+ * Tests whether {@code t} equals {@code p}.
*
- * @param typeVariable the type variable to look up
- * @param typeVarAssigns the map used for the look up
- * @return Type or {@code null} if some variable was not in the map
+ * @param parameterizedType LHS
+ * @param type RHS
+ * @return boolean
* @since 3.2
*/
- private static Type unrollVariableAssignments(TypeVariable<?> typeVariable,
- final Map<TypeVariable<?>, Type> typeVarAssigns) {
- Type result;
- do {
- result = typeVarAssigns.get(typeVariable);
- if (result instanceof TypeVariable<?> && !result.equals(typeVariable)) {
- typeVariable = (TypeVariable<?>) result;
- continue;
+ private static boolean equals(final ParameterizedType parameterizedType, final Type type) {
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType other = (ParameterizedType) type;
+ if (equals(parameterizedType.getRawType(), other.getRawType())
+ && equals(parameterizedType.getOwnerType(), other.getOwnerType())) {
+ return equals(parameterizedType.getActualTypeArguments(), other.getActualTypeArguments());
}
- break;
- } while (true);
- return result;
+ }
+ return false;
}
/**
- * Tests if the subject type may be implicitly cast to the target
- * generic array type following the Java generics rules.
+ * Tests equality of types.
*
- * @param type the subject type to be assigned to the target type
- * @param toGenericArrayType the target generic array type
- * @param typeVarAssigns a map with type variables
- * @return {@code true} if {@code type} is assignable to
- * {@code toGenericArrayType}.
+ * @param type1 the first type
+ * @param type2 the second type
+ * @return boolean
+ * @since 3.2
*/
- private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType,
- final Map<TypeVariable<?>, Type> typeVarAssigns) {
- if (type == null) {
+ public static boolean equals(final Type type1, final Type type2) {
+ if (Objects.equals(type1, type2)) {
return true;
}
-
- // only a null type can be assigned to null type which
- // would have cause the previous to return true
- if (toGenericArrayType == null) {
- return false;
- }
-
- // all types are assignable to themselves
- if (toGenericArrayType.equals(type)) {
- return true;
+ if (type1 instanceof ParameterizedType) {
+ return equals((ParameterizedType) type1, type2);
}
-
- final Type toComponentType = toGenericArrayType.getGenericComponentType();
-
- if (type instanceof Class<?>) {
- final Class<?> cls = (Class<?>) type;
-
- // compare the component types
- return cls.isArray()
- && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns);
+ if (type1 instanceof GenericArrayType) {
+ return equals((GenericArrayType) type1, type2);
}
-
- if (type instanceof GenericArrayType) {
- // compare the component types
- return isAssignable(((GenericArrayType) type).getGenericComponentType(),
- toComponentType, typeVarAssigns);
+ if (type1 instanceof WildcardType) {
+ return equals((WildcardType) type1, type2);
}
+ return false;
+ }
- if (type instanceof WildcardType) {
- // so long as one of the upper bounds is assignable, it's good
- for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
- if (isAssignable(bound, toGenericArrayType)) {
- return true;
+ /**
+ * Tests whether {@code t1} equals {@code t2}.
+ *
+ * @param type1 LHS
+ * @param type2 RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final Type[] type1, final Type[] type2) {
+ if (type1.length == type2.length) {
+ for (int i = 0; i < type1.length; i++) {
+ if (!equals(type1[i], type2[i])) {
+ return false;
}
}
-
- return false;
+ return true;
}
+ return false;
+ }
- if (type instanceof TypeVariable<?>) {
- // probably should remove the following logic and just return false.
- // type variables cannot specify arrays as bounds.
- for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
- if (isAssignable(bound, toGenericArrayType)) {
- return true;
- }
- }
+ /**
+ * Tests whether {@code t} equals {@code w}.
+ *
+ * @param wildcardType LHS
+ * @param type RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final WildcardType wildcardType, final Type type) {
+ if (type instanceof WildcardType) {
+ final WildcardType other = (WildcardType) type;
+ return equals(getImplicitLowerBounds(wildcardType), getImplicitLowerBounds(other))
+ && equals(getImplicitUpperBounds(wildcardType), getImplicitUpperBounds(other));
+ }
+ return false;
+ }
- return false;
+ /**
+ * Helper method to establish the formal parameters for a parameterized type.
+ *
+ * @param mappings map containing the assignments
+ * @param variables expected map keys
+ * @return array of map values corresponding to specified keys
+ */
+ private static Type[] extractTypeArgumentsFrom(final Map<TypeVariable<?>, Type> mappings, final TypeVariable<?>[] variables) {
+ final Type[] result = new Type[variables.length];
+ int index = 0;
+ for (final TypeVariable<?> var : variables) {
+ Validate.isTrue(mappings.containsKey(var), "missing argument mapping for %s", toString(var));
+ result[index++] = mappings.get(var);
}
+ return result;
+ }
- if (type instanceof ParameterizedType) {
- // the raw type of a parameterized type is never an array or
- // generic array, otherwise the declaration would look like this:
- // Collection[]< ? extends String > collection;
- return false;
+ private static int[] findRecursiveTypes(final ParameterizedType parameterizedType) {
+ final Type[] filteredArgumentTypes = Arrays.copyOf(parameterizedType.getActualTypeArguments(),
+ parameterizedType.getActualTypeArguments().length);
+ int[] indexesToRemove = {};
+ for (int i = 0; i < filteredArgumentTypes.length; i++) {
+ if (filteredArgumentTypes[i] instanceof TypeVariable<?>) {
+ if (containsVariableTypeSameParametrizedTypeBound(((TypeVariable<?>) filteredArgumentTypes[i]),
+ parameterizedType)) {
+ indexesToRemove = ArrayUtils.add(indexesToRemove, i);
+ }
+ }
}
+ return indexesToRemove;
+ }
- throw new IllegalStateException("found an unhandled type: " + type);
+ /**
+ * Creates a generic array type instance.
+ *
+ * @param componentType the type of the elements of the array. For example the component type of {@code boolean[]}
+ * is {@code boolean}
+ * @return {@link GenericArrayType}
+ * @since 3.2
+ */
+ public static GenericArrayType genericArrayType(final Type componentType) {
+ return new GenericArrayTypeImpl(Validate.notNull(componentType, "componentType is null"));
}
/**
- * Tests if the subject type may be implicitly cast to the target
- * wildcard type following the Java generics rules.
+ * Formats a {@link GenericArrayType} as a {@link String}.
*
- * @param type the subject type to be assigned to the target type
- * @param toWildcardType the target wildcard type
- * @param typeVarAssigns a map with type variables
- * @return {@code true} if {@code type} is assignable to
- * {@code toWildcardType}.
+ * @param genericArrayType {@code GenericArrayType} to format
+ * @return String
+ * @since 3.2
*/
- private static boolean isAssignable(final Type type, final WildcardType toWildcardType,
- final Map<TypeVariable<?>, Type> typeVarAssigns) {
- if (type == null) {
- return true;
- }
+ private static String genericArrayTypeToString(final GenericArrayType genericArrayType) {
+ return String.format("%s[]", toString(genericArrayType.getGenericComponentType()));
+ }
- // only a null type can be assigned to null type which
- // would have cause the previous to return true
- if (toWildcardType == null) {
- return false;
+ /**
+ * Gets the array component type of {@code type}.
+ *
+ * @param type the type to be checked
+ * @return component type or null if type is not an array type
+ */
+ public static Type getArrayComponentType(final Type type) {
+ if (type instanceof Class<?>) {
+ final Class<?> cls = (Class<?>) type;
+ return cls.isArray() ? cls.getComponentType() : null;
}
-
- // all types are assignable to themselves
- if (toWildcardType.equals(type)) {
- return true;
+ if (type instanceof GenericArrayType) {
+ return ((GenericArrayType) type).getGenericComponentType();
}
+ return null;
+ }
- final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
- final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);
-
- if (type instanceof WildcardType) {
- final WildcardType wildcardType = (WildcardType) type;
- final Type[] upperBounds = getImplicitUpperBounds(wildcardType);
- final Type[] lowerBounds = getImplicitLowerBounds(wildcardType);
+ /**
+ * Gets the closest parent type to the
+ * super class specified by {@code superClass}.
+ *
+ * @param cls the class in question
+ * @param superClass the super class
+ * @return the closes parent type
+ */
+ private static Type getClosestParentType(final Class<?> cls, final Class<?> superClass) {
+ // only look at the interfaces if the super class is also an interface
+ if (superClass.isInterface()) {
+ // get the generic interfaces of the subject class
+ final Type[] interfaceTypes = cls.getGenericInterfaces();
+ // will hold the best generic interface match found
+ Type genericInterface = null;
- for (Type toBound : toUpperBounds) {
- // if there are assignments for unresolved type variables,
- // now's the time to substitute them.
- toBound = substituteTypeVariables(toBound, typeVarAssigns);
+ // find the interface closest to the super class
+ for (final Type midType : interfaceTypes) {
+ Class<?> midClass = null;
- // each upper bound of the subject type has to be assignable to
- // each
- // upper bound of the target type
- for (final Type bound : upperBounds) {
- if (!isAssignable(bound, toBound, typeVarAssigns)) {
- return false;
- }
+ if (midType instanceof ParameterizedType) {
+ midClass = getRawType((ParameterizedType) midType);
+ } else if (midType instanceof Class<?>) {
+ midClass = (Class<?>) midType;
+ } else {
+ throw new IllegalStateException("Unexpected generic"
+ + " interface type found: " + midType);
}
- }
- for (Type toBound : toLowerBounds) {
- // if there are assignments for unresolved type variables,
- // now's the time to substitute them.
- toBound = substituteTypeVariables(toBound, typeVarAssigns);
-
- // each lower bound of the target type has to be assignable to
- // each
- // lower bound of the subject type
- for (final Type bound : lowerBounds) {
- if (!isAssignable(toBound, bound, typeVarAssigns)) {
- return false;
- }
+ // check if this interface is further up the inheritance chain
+ // than the previously found match
+ if (isAssignable(midClass, superClass)
+ && isAssignable(genericInterface, (Type) midClass)) {
+ genericInterface = midType;
}
}
- return true;
- }
- for (final Type toBound : toUpperBounds) {
- // if there are assignments for unresolved type variables,
- // now's the time to substitute them.
- if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns),
- typeVarAssigns)) {
- return false;
+ // found a match?
+ if (genericInterface != null) {
+ return genericInterface;
}
}
- for (final Type toBound : toLowerBounds) {
- // if there are assignments for unresolved type variables,
- // now's the time to substitute them.
- if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type,
- typeVarAssigns)) {
- return false;
- }
+ // none of the interfaces were descendants of the target class, so the
+ // super class has to be one, instead
+ return cls.getGenericSuperclass();
+ }
+
+ /**
+ * Gets an array containing the sole type of {@link Object} if
+ * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it
+ * returns the result of {@link TypeVariable#getBounds()} passed into
+ * {@link #normalizeUpperBounds}.
+ *
+ * @param typeVariable the subject type variable, not {@code null}
+ * @return a non-empty array containing the bounds of the type variable.
+ */
+ public static Type[] getImplicitBounds(final TypeVariable<?> typeVariable) {
+ Validate.notNull(typeVariable, "typeVariable is null");
+ final Type[] bounds = typeVariable.getBounds();
+
+ return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
+ }
+
+ /**
+ * Gets an array containing a single value of {@code null} if
+ * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise,
+ * it returns the result of {@link WildcardType#getLowerBounds()}.
+ *
+ * @param wildcardType the subject wildcard type, not {@code null}
+ * @return a non-empty array containing the lower bounds of the wildcard
+ * type.
+ */
+ public static Type[] getImplicitLowerBounds(final WildcardType wildcardType) {
+ Validate.notNull(wildcardType, "wildcardType is null");
+ final Type[] bounds = wildcardType.getLowerBounds();
+
+ return bounds.length == 0 ? new Type[] { null } : bounds;
+ }
+
+ /**
+ * Gets an array containing the sole value of {@link Object} if
+ * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise,
+ * it returns the result of {@link WildcardType#getUpperBounds()}
+ * passed into {@link #normalizeUpperBounds}.
+ *
+ * @param wildcardType the subject wildcard type, not {@code null}
+ * @return a non-empty array containing the upper bounds of the wildcard
+ * type.
+ */
+ public static Type[] getImplicitUpperBounds(final WildcardType wildcardType) {
+ Validate.notNull(wildcardType, "wildcardType is null");
+ final Type[] bounds = wildcardType.getUpperBounds();
+
+ return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
+ }
+
+ /**
+ * Transforms the passed in type to a {@link Class} object. Type-checking method of convenience.
+ *
+ * @param parameterizedType the type to be converted
+ * @return the corresponding {@code Class} object
+ * @throws IllegalStateException if the conversion fails
+ */
+ private static Class<?> getRawType(final ParameterizedType parameterizedType) {
+ final Type rawType = parameterizedType.getRawType();
+
+ // check if raw type is a Class object
+ // not currently necessary, but since the return type is Type instead of
+ // Class, there's enough reason to believe that future versions of Java
+ // may return other Type implementations. And type-safety checking is
+ // rarely a bad idea.
+ if (!(rawType instanceof Class<?>)) {
+ throw new IllegalStateException("Wait... What!? Type of rawType: " + rawType);
}
- return true;
+
+ return (Class<?>) rawType;
}
/**
- * Tests if the subject type may be implicitly cast to the target type
- * variable following the Java generics rules.
+ * Gets the raw type of a Java type, given its context. Primarily for use
+ * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do
+ * not know the runtime type of {@code type}: if you know you have a
+ * {@link Class} instance, it is already raw; if you know you have a
+ * {@link ParameterizedType}, its raw type is only a method call away.
*
- * @param type the subject type to be assigned to the target type
- * @param toTypeVariable the target type variable
- * @param typeVarAssigns a map with type variables
- * @return {@code true} if {@code type} is assignable to
- * {@code toTypeVariable}.
+ * @param type to resolve
+ * @param assigningType type to be resolved against
+ * @return the resolved {@link Class} object or {@code null} if
+ * the type could not be resolved
*/
- private static boolean isAssignable(final Type type, final TypeVariable<?> toTypeVariable,
- final Map<TypeVariable<?>, Type> typeVarAssigns) {
- if (type == null) {
- return true;
+ public static Class<?> getRawType(final Type type, final Type assigningType) {
+ if (type instanceof Class<?>) {
+ // it is raw, no problem
+ return (Class<?>) type;
}
- // only a null type can be assigned to null type which
- // would have cause the previous to return true
- if (toTypeVariable == null) {
- return false;
+ if (type instanceof ParameterizedType) {
+ // simple enough to get the raw type of a ParameterizedType
+ return getRawType((ParameterizedType) type);
}
- // all types are assignable to themselves
- if (toTypeVariable.equals(type)) {
- return true;
+ if (type instanceof TypeVariable<?>) {
+ if (assigningType == null) {
+ return null;
+ }
+
+ // get the entity declaring this type variable
+ final Object genericDeclaration = ((TypeVariable<?>) type).getGenericDeclaration();
+
+ // can't get the raw type of a method- or constructor-declared type
+ // variable
+ if (!(genericDeclaration instanceof Class<?>)) {
+ return null;
+ }
+
+ // get the type arguments for the declaring class/interface based
+ // on the enclosing type
+ final Map<TypeVariable<?>, Type> typeVarAssigns = getTypeArguments(assigningType,
+ (Class<?>) genericDeclaration);
+
+ // enclosingType has to be a subclass (or subinterface) of the
+ // declaring type
+ if (typeVarAssigns == null) {
+ return null;
+ }
+
+ // get the argument assigned to this type variable
+ final Type typeArgument = typeVarAssigns.get(type);
+
+ if (typeArgument == null) {
+ return null;
+ }
+
+ // get the argument for this type variable
+ return getRawType(typeArgument, assigningType);
}
- if (type instanceof TypeVariable<?>) {
- // a type variable is assignable to another type variable, if
- // and only if the former is the latter, extends the latter, or
- // is otherwise a descendant of the latter.
- final Type[] bounds = getImplicitBounds((TypeVariable<?>) type);
+ if (type instanceof GenericArrayType) {
+ // get raw component type
+ final Class<?> rawComponentType = getRawType(((GenericArrayType) type)
+ .getGenericComponentType(), assigningType);
- for (final Type bound : bounds) {
- if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
- return true;
- }
- }
+ // create array type from raw component type and return its class
+ return Array.newInstance(rawComponentType, 0).getClass();
}
- if (type instanceof Class<?> || type instanceof ParameterizedType
- || type instanceof GenericArrayType || type instanceof WildcardType) {
- return false;
+ // (hand-waving) this is not the method you're looking for
+ if (type instanceof WildcardType) {
+ return null;
}
- throw new IllegalStateException("found an unhandled type: " + type);
+ throw new IllegalArgumentException("unknown type: " + type);
}
/**
- * Finds the mapping for {@code type} in {@code typeVarAssigns}.
+ * Gets a map of the type arguments of a class in the context of {@code toClass}.
*
- * @param type the type to be replaced
- * @param typeVarAssigns the map with type variables
- * @return the replaced type
- * @throws IllegalArgumentException if the type cannot be substituted
+ * @param cls the class in question
+ * @param toClass the context class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
*/
- private static Type substituteTypeVariables(final Type type, final Map<TypeVariable<?>, Type> typeVarAssigns) {
- if (type instanceof TypeVariable<?> && typeVarAssigns != null) {
- final Type replacementType = typeVarAssigns.get(type);
+ private static Map<TypeVariable<?>, Type> getTypeArguments(Class<?> cls, final Class<?> toClass,
+ final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
+ // make sure they're assignable
+ if (!isAssignable(cls, toClass)) {
+ return null;
+ }
- if (replacementType == null) {
- throw new IllegalArgumentException("missing assignment type for type variable "
- + type);
+ // can't work with primitives
+ if (cls.isPrimitive()) {
+ // both classes are primitives?
+ if (toClass.isPrimitive()) {
+ // dealing with widening here. No type arguments to be
+ // harvested with these two types.
+ return new HashMap<>();
}
- return replacementType;
+
+ // work with wrapper the wrapper class instead of the primitive
+ cls = ClassUtils.primitiveToWrapper(cls);
}
- return type;
+
+ // create a copy of the incoming map, or an empty one if it's null
+ final HashMap<TypeVariable<?>, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>()
+ : new HashMap<>(subtypeVarAssigns);
+
+ // has target class been reached?
+ if (toClass.equals(cls)) {
+ return typeVarAssigns;
+ }
+
+ // walk the inheritance hierarchy until the target class is reached
+ return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
}
/**
@@ -753,6 +864,61 @@ public class TypeUtils {
}
/**
+ * Gets a map of the type arguments of a parameterized type in the context of {@code toClass}.
+ *
+ * @param parameterizedType the parameterized type
+ * @param toClass the class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map<TypeVariable<?>, Type> getTypeArguments(
+ final ParameterizedType parameterizedType, final Class<?> toClass,
+ final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
+ final Class<?> cls = getRawType(parameterizedType);
+
+ // make sure they're assignable
+ if (!isAssignable(cls, toClass)) {
+ return null;
+ }
+
+ final Type ownerType = parameterizedType.getOwnerType();
+ Map<TypeVariable<?>, Type> typeVarAssigns;
+
+ if (ownerType instanceof ParameterizedType) {
+ // get the owner type arguments first
+ final ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType;
+ typeVarAssigns = getTypeArguments(parameterizedOwnerType,
+ getRawType(parameterizedOwnerType), subtypeVarAssigns);
+ } else {
+ // no owner, prep the type variable assignments map
+ typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>()
+ : new HashMap<>(subtypeVarAssigns);
+ }
+
+ // get the subject parameterized type's arguments
+ final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+ // and get the corresponding type variables from the raw class
+ final TypeVariable<?>[] typeParams = cls.getTypeParameters();
+
+ // map the arguments to their respective type variables
+ for (int i = 0; i < typeParams.length; i++) {
+ final Type typeArg = typeArgs[i];
+ typeVarAssigns.put(
+ typeParams[i],
+ typeVarAssigns.getOrDefault(typeArg, typeArg)
+ );
+ }
+
+ if (toClass.equals(cls)) {
+ // target class has been reached. Done.
+ return typeVarAssigns;
+ }
+
+ // walk the inheritance hierarchy until the target class is reached
+ return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
+ }
+
+ /**
* Gets the type arguments of a class/interface based on a subtype. For
* instance, this method will determine that both of the parameters for the
* interface {@link Map} are {@link Object} for the subtype
@@ -844,628 +1010,539 @@ public class TypeUtils {
}
/**
- * Gets a map of the type arguments of a parameterized type in the context of {@code toClass}.
- *
- * @param parameterizedType the parameterized type
- * @param toClass the class
- * @param subtypeVarAssigns a map with type variables
- * @return the {@code Map} with type arguments
- */
- private static Map<TypeVariable<?>, Type> getTypeArguments(
- final ParameterizedType parameterizedType, final Class<?> toClass,
- final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
- final Class<?> cls = getRawType(parameterizedType);
-
- // make sure they're assignable
- if (!isAssignable(cls, toClass)) {
- return null;
- }
-
- final Type ownerType = parameterizedType.getOwnerType();
- Map<TypeVariable<?>, Type> typeVarAssigns;
-
- if (ownerType instanceof ParameterizedType) {
- // get the owner type arguments first
- final ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType;
- typeVarAssigns = getTypeArguments(parameterizedOwnerType,
- getRawType(parameterizedOwnerType), subtypeVarAssigns);
- } else {
- // no owner, prep the type variable assignments map
- typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>()
- : new HashMap<>(subtypeVarAssigns);
- }
-
- // get the subject parameterized type's arguments
- final Type[] typeArgs = parameterizedType.getActualTypeArguments();
- // and get the corresponding type variables from the raw class
- final TypeVariable<?>[] typeParams = cls.getTypeParameters();
-
- // map the arguments to their respective type variables
- for (int i = 0; i < typeParams.length; i++) {
- final Type typeArg = typeArgs[i];
- typeVarAssigns.put(
- typeParams[i],
- typeVarAssigns.getOrDefault(typeArg, typeArg)
- );
- }
-
- if (toClass.equals(cls)) {
- // target class has been reached. Done.
- return typeVarAssigns;
- }
-
- // walk the inheritance hierarchy until the target class is reached
- return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
- }
-
- /**
- * Gets a map of the type arguments of a class in the context of {@code toClass}.
+ * Tests whether the specified type denotes an array type.
*
- * @param cls the class in question
- * @param toClass the context class
- * @param subtypeVarAssigns a map with type variables
- * @return the {@code Map} with type arguments
+ * @param type the type to be checked
+ * @return {@code true} if {@code type} is an array class or a {@link GenericArrayType}.
*/
- private static Map<TypeVariable<?>, Type> getTypeArguments(Class<?> cls, final Class<?> toClass,
- final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
- // make sure they're assignable
- if (!isAssignable(cls, toClass)) {
- return null;
- }
-
- // can't work with primitives
- if (cls.isPrimitive()) {
- // both classes are primitives?
- if (toClass.isPrimitive()) {
- // dealing with widening here. No type arguments to be
- // harvested with these two types.
- return new HashMap<>();
- }
-
- // work with wrapper the wrapper class instead of the primitive
- cls = ClassUtils.primitiveToWrapper(cls);
- }
-
- // create a copy of the incoming map, or an empty one if it's null
- final HashMap<TypeVariable<?>, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>()
- : new HashMap<>(subtypeVarAssigns);
-
- // has target class been reached?
- if (toClass.equals(cls)) {
- return typeVarAssigns;
- }
-
- // walk the inheritance hierarchy until the target class is reached
- return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
+ public static boolean isArrayType(final Type type) {
+ return type instanceof GenericArrayType || type instanceof Class<?> && ((Class<?>) type).isArray();
}
/**
- * Tries to determine the type arguments of a class/interface based on a
- * super parameterized type's type arguments. This method is the inverse of
- * {@link #getTypeArguments(Type, Class)} which gets a class/interface's
- * type arguments based on a subtype. It is far more limited in determining
- * the type arguments for the subject class's type variables in that it can
- * only determine those parameters that map from the subject {@link Class}
- * object to the supertype.
- *
- * <p>
- * Example: {@link java.util.TreeSet
- * TreeSet} sets its parameter as the parameter for
- * {@link java.util.NavigableSet NavigableSet}, which in turn sets the
- * parameter of {@link java.util.SortedSet}, which in turn sets the
- * parameter of {@link Set}, which in turn sets the parameter of
- * {@link java.util.Collection}, which in turn sets the parameter of
- * {@link java.lang.Iterable}. Since {@code TreeSet}'s parameter maps
- * (indirectly) to {@code Iterable}'s parameter, it will be able to
- * determine that based on the super type {@code Iterable<? extends
- * Map<Integer, ? extends Collection<?>>>}, the parameter of
- * {@code TreeSet} is {@code ? extends Map<Integer, ? extends
- * Collection<?>>}.
- * </p>
+ * Tests if the subject type may be implicitly cast to the target class
+ * following the Java generics rules.
*
- * @param cls the class whose type parameters are to be determined, not {@code null}
- * @param superType the super type from which {@code cls}'s type
- * arguments are to be determined, not {@code null}
- * @return a {@code Map} of the type assignments that could be determined
- * for the type variables in each type in the inheritance hierarchy from
- * {@code type} to {@code toClass} inclusive.
+ * @param type the subject type to be assigned to the target type
+ * @param toClass the target class
+ * @return {@code true} if {@code type} is assignable to {@code toClass}.
*/
- public static Map<TypeVariable<?>, Type> determineTypeArguments(final Class<?> cls,
- final ParameterizedType superType) {
- Validate.notNull(cls, "cls is null");
- Validate.notNull(superType, "superType is null");
+ private static boolean isAssignable(final Type type, final Class<?> toClass) {
+ if (type == null) {
+ // consistency with ClassUtils.isAssignable() behavior
+ return toClass == null || !toClass.isPrimitive();
+ }
- final Class<?> superClass = getRawType(superType);
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toClass == null) {
+ return false;
+ }
- // compatibility check
- if (!isAssignable(cls, superClass)) {
- return null;
+ // all types are assignable to themselves
+ if (toClass.equals(type)) {
+ return true;
}
- if (cls.equals(superClass)) {
- return getTypeArguments(superType, superClass, null);
+ if (type instanceof Class<?>) {
+ // just comparing two classes
+ return ClassUtils.isAssignable((Class<?>) type, toClass);
}
- // get the next class in the inheritance hierarchy
- final Type midType = getClosestParentType(cls, superClass);
+ if (type instanceof ParameterizedType) {
+ // only have to compare the raw type to the class
+ return isAssignable(getRawType((ParameterizedType) type), toClass);
+ }
- // can only be a class or a parameterized type
- if (midType instanceof Class<?>) {
- return determineTypeArguments((Class<?>) midType, superType);
+ // *
+ if (type instanceof TypeVariable<?>) {
+ // if any of the bounds are assignable to the class, then the
+ // type is assignable to the class.
+ for (final Type bound : ((TypeVariable<?>) type).getBounds()) {
+ if (isAssignable(bound, toClass)) {
+ return true;
+ }
+ }
+
+ return false;
}
- final ParameterizedType midParameterizedType = (ParameterizedType) midType;
- final Class<?> midClass = getRawType(midParameterizedType);
- // get the type variables of the mid class that map to the type
- // arguments of the super class
- final Map<TypeVariable<?>, Type> typeVarAssigns = determineTypeArguments(midClass, superType);
- // map the arguments of the mid type to the class type variables
- mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns);
+ // the only classes to which a generic array type can be assigned
+ // are class Object and array classes
+ if (type instanceof GenericArrayType) {
+ return toClass.equals(Object.class)
+ || toClass.isArray()
+ && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass
+ .getComponentType());
+ }
- return typeVarAssigns;
+ // wildcard types are not assignable to a class (though one would think
+ // "? super Object" would be assignable to Object)
+ if (type instanceof WildcardType) {
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
}
/**
- * Maps type variables.
+ * Tests if the subject type may be implicitly cast to the target
+ * generic array type following the Java generics rules.
*
- * @param <T> the generic type of the class in question
- * @param cls the class in question
- * @param parameterizedType the parameterized type
- * @param typeVarAssigns the map to be filled
+ * @param type the subject type to be assigned to the target type
+ * @param toGenericArrayType the target generic array type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toGenericArrayType}.
*/
- private static <T> void mapTypeVariablesToArguments(final Class<T> cls,
- final ParameterizedType parameterizedType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
- // capture the type variables from the owner type that have assignments
- final Type ownerType = parameterizedType.getOwnerType();
-
- if (ownerType instanceof ParameterizedType) {
- // recursion to make sure the owner's owner type gets processed
- mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns);
+ private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType,
+ final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
}
- // parameterizedType is a generic interface/class (or it's in the owner
- // hierarchy of said interface/class) implemented/extended by the class
- // cls. Find out which type variables of cls are type arguments of
- // parameterizedType:
- final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toGenericArrayType == null) {
+ return false;
+ }
- // of the cls's type variables that are arguments of parameterizedType,
- // find out which ones can be determined from the super type's arguments
- final TypeVariable<?>[] typeVars = getRawType(parameterizedType).getTypeParameters();
+ // all types are assignable to themselves
+ if (toGenericArrayType.equals(type)) {
+ return true;
+ }
- // use List view of type parameters of cls so the contains() method can be used:
- final List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls
- .getTypeParameters());
+ final Type toComponentType = toGenericArrayType.getGenericComponentType();
- for (int i = 0; i < typeArgs.length; i++) {
- final TypeVariable<?> typeVar = typeVars[i];
- final Type typeArg = typeArgs[i];
+ if (type instanceof Class<?>) {
+ final Class<?> cls = (Class<?>) type;
- // argument of parameterizedType is a type variable of cls
- if (typeVarList.contains(typeArg)
- // type variable of parameterizedType has an assignment in
- // the super type.
- && typeVarAssigns.containsKey(typeVar)) {
- // map the assignment to the cls's type variable
- typeVarAssigns.put((TypeVariable<?>) typeArg, typeVarAssigns.get(typeVar));
- }
+ // compare the component types
+ return cls.isArray()
+ && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns);
}
- }
-
- /**
- * Gets the closest parent type to the
- * super class specified by {@code superClass}.
- *
- * @param cls the class in question
- * @param superClass the super class
- * @return the closes parent type
- */
- private static Type getClosestParentType(final Class<?> cls, final Class<?> superClass) {
- // only look at the interfaces if the super class is also an interface
- if (superClass.isInterface()) {
- // get the generic interfaces of the subject class
- final Type[] interfaceTypes = cls.getGenericInterfaces();
- // will hold the best generic interface match found
- Type genericInterface = null;
- // find the interface closest to the super class
- for (final Type midType : interfaceTypes) {
- Class<?> midClass = null;
+ if (type instanceof GenericArrayType) {
+ // compare the component types
+ return isAssignable(((GenericArrayType) type).getGenericComponentType(),
+ toComponentType, typeVarAssigns);
+ }
- if (midType instanceof ParameterizedType) {
- midClass = getRawType((ParameterizedType) midType);
- } else if (midType instanceof Class<?>) {
- midClass = (Class<?>) midType;
- } else {
- throw new IllegalStateException("Unexpected generic"
- + " interface type found: " + midType);
+ if (type instanceof WildcardType) {
+ // so long as one of the upper bounds is assignable, it's good
+ for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
+ if (isAssignable(bound, toGenericArrayType)) {
+ return true;
}
+ }
- // check if this interface is further up the inheritance chain
- // than the previously found match
- if (isAssignable(midClass, superClass)
- && isAssignable(genericInterface, (Type) midClass)) {
- genericInterface = midType;
+ return false;
+ }
+
+ if (type instanceof TypeVariable<?>) {
+ // probably should remove the following logic and just return false.
+ // type variables cannot specify arrays as bounds.
+ for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
+ if (isAssignable(bound, toGenericArrayType)) {
+ return true;
}
}
- // found a match?
- if (genericInterface != null) {
- return genericInterface;
- }
+ return false;
}
- // none of the interfaces were descendants of the target class, so the
- // super class has to be one, instead
- return cls.getGenericSuperclass();
- }
-
- /**
- * Tests if the given value can be assigned to the target type
- * following the Java generics rules.
- *
- * @param value the value to be checked
- * @param type the target type
- * @return {@code true} if {@code value} is an instance of {@code type}.
- */
- public static boolean isInstance(final Object value, final Type type) {
- if (type == null) {
+ if (type instanceof ParameterizedType) {
+ // the raw type of a parameterized type is never an array or
+ // generic array, otherwise the declaration would look like this:
+ // Collection[]< ? extends String > collection;
return false;
}
- return value == null ? !(type instanceof Class<?>) || !((Class<?>) type).isPrimitive()
- : isAssignable(value.getClass(), type, null);
+ throw new IllegalStateException("found an unhandled type: " + type);
}
/**
- * Strips out the redundant upper bound types in type
- * variable types and wildcard types (or it would with wildcard types if
- * multiple upper bounds were allowed).
- *
- * <p>
- * Example, with the variable type declaration:
- * </p>
- *
- * <pre><K extends java.util.Collection<String> &
- * java.util.List<String>></pre>
- *
- * <p>
- * since {@code List} is a subinterface of {@code Collection},
- * this method will return the bounds as if the declaration had been:
- * </p>
- *
- * <pre><K extends java.util.List<String>></pre>
+ * Tests if the subject type may be implicitly cast to the target
+ * parameterized type following the Java generics rules.
*
- * @param bounds an array of types representing the upper bounds of either
- * {@link WildcardType} or {@link TypeVariable}, not {@code null}.
- * @return an array containing the values from {@code bounds} minus the
- * redundant types.
+ * @param type the subject type to be assigned to the target type
+ * @param toParameterizedType the target parameterized type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
*/
- public static Type[] normalizeUpperBounds(final Type[] bounds) {
- Validate.notNull(bounds, "null value specified for bounds array");
- // don't bother if there's only one (or none) type
- if (bounds.length < 2) {
- return bounds;
+ private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType,
+ final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toParameterizedType == null) {
+ return false;
}
- final Set<Type> types = new HashSet<>(bounds.length);
+ // all types are assignable to themselves
+ if (toParameterizedType.equals(type)) {
+ return true;
+ }
- for (final Type type1 : bounds) {
- boolean subtypeFound = false;
+ // get the target type's raw type
+ final Class<?> toClass = getRawType(toParameterizedType);
+ // get the subject type's type arguments including owner type arguments
+ // and supertype arguments up to and including the target class.
+ final Map<TypeVariable<?>, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null);
- for (final Type type2 : bounds) {
- if (type1 != type2 && isAssignable(type2, type1, null)) {
- subtypeFound = true;
- break;
- }
+ // null means the two types are not compatible
+ if (fromTypeVarAssigns == null) {
+ return false;
+ }
+
+ // compatible types, but there's no type arguments. this is equivalent
+ // to comparing Map< ?, ? > to Map, and raw types are always assignable
+ // to parameterized types.
+ if (fromTypeVarAssigns.isEmpty()) {
+ return true;
+ }
+
+ // get the target type's type arguments including owner type arguments
+ final Map<TypeVariable<?>, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType,
+ toClass, typeVarAssigns);
+
+ // now to check each type argument
+ for (final TypeVariable<?> var : toTypeVarAssigns.keySet()) {
+ final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
+ final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns);
+
+ if (toTypeArg == null && fromTypeArg instanceof Class) {
+ continue;
}
- if (!subtypeFound) {
- types.add(type1);
+ // parameters must either be absent from the subject type, within
+ // the bounds of the wildcard type, or be an exact match to the
+ // parameters of the target type.
+ if (fromTypeArg != null
+ && !toTypeArg.equals(fromTypeArg)
+ && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg,
+ typeVarAssigns))) {
+ return false;
}
}
-
- return types.toArray(ArrayUtils.EMPTY_TYPE_ARRAY);
+ return true;
}
/**
- * Gets an array containing the sole type of {@link Object} if
- * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it
- * returns the result of {@link TypeVariable#getBounds()} passed into
- * {@link #normalizeUpperBounds}.
+ * Tests if the subject type may be implicitly cast to the target type
+ * following the Java generics rules. If both types are {@link Class}
+ * objects, the method returns the result of
+ * {@link ClassUtils#isAssignable(Class, Class)}.
*
- * @param typeVariable the subject type variable, not {@code null}
- * @return a non-empty array containing the bounds of the type variable.
+ * @param type the subject type to be assigned to the target type
+ * @param toType the target type
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
*/
- public static Type[] getImplicitBounds(final TypeVariable<?> typeVariable) {
- Validate.notNull(typeVariable, "typeVariable is null");
- final Type[] bounds = typeVariable.getBounds();
-
- return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
+ public static boolean isAssignable(final Type type, final Type toType) {
+ return isAssignable(type, toType, null);
}
/**
- * Gets an array containing the sole value of {@link Object} if
- * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise,
- * it returns the result of {@link WildcardType#getUpperBounds()}
- * passed into {@link #normalizeUpperBounds}.
+ * Tests if the subject type may be implicitly cast to the target type
+ * following the Java generics rules.
*
- * @param wildcardType the subject wildcard type, not {@code null}
- * @return a non-empty array containing the upper bounds of the wildcard
- * type.
+ * @param type the subject type to be assigned to the target type
+ * @param toType the target type
+ * @param typeVarAssigns optional map of type variable assignments
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
*/
- public static Type[] getImplicitUpperBounds(final WildcardType wildcardType) {
- Validate.notNull(wildcardType, "wildcardType is null");
- final Type[] bounds = wildcardType.getUpperBounds();
+ private static boolean isAssignable(final Type type, final Type toType,
+ final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ if (toType == null || toType instanceof Class<?>) {
+ return isAssignable(type, (Class<?>) toType);
+ }
- return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
- }
+ if (toType instanceof ParameterizedType) {
+ return isAssignable(type, (ParameterizedType) toType, typeVarAssigns);
+ }
- /**
- * Gets an array containing a single value of {@code null} if
- * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise,
- * it returns the result of {@link WildcardType#getLowerBounds()}.
- *
- * @param wildcardType the subject wildcard type, not {@code null}
- * @return a non-empty array containing the lower bounds of the wildcard
- * type.
- */
- public static Type[] getImplicitLowerBounds(final WildcardType wildcardType) {
- Validate.notNull(wildcardType, "wildcardType is null");
- final Type[] bounds = wildcardType.getLowerBounds();
+ if (toType instanceof GenericArrayType) {
+ return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
+ }
- return bounds.length == 0 ? new Type[] { null } : bounds;
+ if (toType instanceof WildcardType) {
+ return isAssignable(type, (WildcardType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof TypeVariable<?>) {
+ return isAssignable(type, (TypeVariable<?>) toType, typeVarAssigns);
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + toType);
}
/**
- * Determines whether or not specified types satisfy the bounds of their
- * mapped type variables. When a type parameter extends another (such as
- * {@code <T, S extends T>}), uses another as a type parameter (such as
- * {@code <T, S extends Comparable>>}), or otherwise depends on
- * another type variable to be specified, the dependencies must be included
- * in {@code typeVarAssigns}.
+ * Tests if the subject type may be implicitly cast to the target type
+ * variable following the Java generics rules.
*
- * @param typeVarAssigns specifies the potential types to be assigned to the
- * type variables, not {@code null}.
- * @return whether or not the types can be assigned to their respective type
- * variables.
+ * @param type the subject type to be assigned to the target type
+ * @param toTypeVariable the target type variable
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toTypeVariable}.
*/
- public static boolean typesSatisfyVariables(final Map<TypeVariable<?>, Type> typeVarAssigns) {
- Validate.notNull(typeVarAssigns, "typeVarAssigns is null");
- // all types must be assignable to all the bounds of their mapped
- // type variable.
- for (final Map.Entry<TypeVariable<?>, Type> entry : typeVarAssigns.entrySet()) {
- final TypeVariable<?> typeVar = entry.getKey();
- final Type type = entry.getValue();
+ private static boolean isAssignable(final Type type, final TypeVariable<?> toTypeVariable,
+ final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
- for (final Type bound : getImplicitBounds(typeVar)) {
- if (!isAssignable(type, substituteTypeVariables(bound, typeVarAssigns),
- typeVarAssigns)) {
- return false;
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toTypeVariable == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toTypeVariable.equals(type)) {
+ return true;
+ }
+
+ if (type instanceof TypeVariable<?>) {
+ // a type variable is assignable to another type variable, if
+ // and only if the former is the latter, extends the latter, or
+ // is otherwise a descendant of the latter.
+ final Type[] bounds = getImplicitBounds((TypeVariable<?>) type);
+
+ for (final Type bound : bounds) {
+ if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
+ return true;
}
}
}
- return true;
- }
-
- /**
- * Transforms the passed in type to a {@link Class} object. Type-checking method of convenience.
- *
- * @param parameterizedType the type to be converted
- * @return the corresponding {@code Class} object
- * @throws IllegalStateException if the conversion fails
- */
- private static Class<?> getRawType(final ParameterizedType parameterizedType) {
- final Type rawType = parameterizedType.getRawType();
- // check if raw type is a Class object
- // not currently necessary, but since the return type is Type instead of
- // Class, there's enough reason to believe that future versions of Java
- // may return other Type implementations. And type-safety checking is
- // rarely a bad idea.
- if (!(rawType instanceof Class<?>)) {
- throw new IllegalStateException("Wait... What!? Type of rawType: " + rawType);
+ if (type instanceof Class<?> || type instanceof ParameterizedType
+ || type instanceof GenericArrayType || type instanceof WildcardType) {
+ return false;
}
- return (Class<?>) rawType;
+ throw new IllegalStateException("found an unhandled type: " + type);
}
/**
- * Gets the raw type of a Java type, given its context. Primarily for use
- * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do
- * not know the runtime type of {@code type}: if you know you have a
- * {@link Class} instance, it is already raw; if you know you have a
- * {@link ParameterizedType}, its raw type is only a method call away.
+ * Tests if the subject type may be implicitly cast to the target
+ * wildcard type following the Java generics rules.
*
- * @param type to resolve
- * @param assigningType type to be resolved against
- * @return the resolved {@link Class} object or {@code null} if
- * the type could not be resolved
+ * @param type the subject type to be assigned to the target type
+ * @param toWildcardType the target wildcard type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toWildcardType}.
*/
- public static Class<?> getRawType(final Type type, final Type assigningType) {
- if (type instanceof Class<?>) {
- // it is raw, no problem
- return (Class<?>) type;
+ private static boolean isAssignable(final Type type, final WildcardType toWildcardType,
+ final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
}
- if (type instanceof ParameterizedType) {
- // simple enough to get the raw type of a ParameterizedType
- return getRawType((ParameterizedType) type);
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toWildcardType == null) {
+ return false;
}
- if (type instanceof TypeVariable<?>) {
- if (assigningType == null) {
- return null;
- }
+ // all types are assignable to themselves
+ if (toWildcardType.equals(type)) {
+ return true;
+ }
- // get the entity declaring this type variable
- final Object genericDeclaration = ((TypeVariable<?>) type).getGenericDeclaration();
+ final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
+ final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);
- // can't get the raw type of a method- or constructor-declared type
- // variable
- if (!(genericDeclaration instanceof Class<?>)) {
- return null;
- }
+ if (type instanceof WildcardType) {
+ final WildcardType wildcardType = (WildcardType) type;
+ final Type[] upperBounds = getImplicitUpperBounds(wildcardType);
+ final Type[] lowerBounds = getImplicitLowerBounds(wildcardType);
- // get the type arguments for the declaring class/interface based
- // on the enclosing type
- final Map<TypeVariable<?>, Type> typeVarAssigns = getTypeArguments(assigningType,
- (Class<?>) genericDeclaration);
+ for (Type toBound : toUpperBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ toBound = substituteTypeVariables(toBound, typeVarAssigns);
- // enclosingType has to be a subclass (or subinterface) of the
- // declaring type
- if (typeVarAssigns == null) {
- return null;
+ // each upper bound of the subject type has to be assignable to
+ // each
+ // upper bound of the target type
+ for (final Type bound : upperBounds) {
+ if (!isAssignable(bound, toBound, typeVarAssigns)) {
+ return false;
+ }
+ }
}
- // get the argument assigned to this type variable
- final Type typeArgument = typeVarAssigns.get(type);
+ for (Type toBound : toLowerBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ toBound = substituteTypeVariables(toBound, typeVarAssigns);
- if (typeArgument == null) {
- return null;
+ // each lower bound of the target type has to be assignable to
+ // each
+ // lower bound of the subject type
+ for (final Type bound : lowerBounds) {
+ if (!isAssignable(toBound, bound, typeVarAssigns)) {
+ return false;
+ }
+ }
}
-
- // get the argument for this type variable
- return getRawType(typeArgument, assigningType);
+ return true;
}
- if (type instanceof GenericArrayType) {
- // get raw component type
- final Class<?> rawComponentType = getRawType(((GenericArrayType) type)
- .getGenericComponentType(), assigningType);
-
- // create array type from raw component type and return its class
- return Array.newInstance(rawComponentType, 0).getClass();
+ for (final Type toBound : toUpperBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns),
+ typeVarAssigns)) {
+ return false;
+ }
}
- // (hand-waving) this is not the method you're looking for
- if (type instanceof WildcardType) {
- return null;
+ for (final Type toBound : toLowerBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type,
+ typeVarAssigns)) {
+ return false;
+ }
}
-
- throw new IllegalArgumentException("unknown type: " + type);
- }
-
- /**
- * Tests whether the specified type denotes an array type.
- *
- * @param type the type to be checked
- * @return {@code true} if {@code type} is an array class or a {@link GenericArrayType}.
- */
- public static boolean isArrayType(final Type type) {
- return type instanceof GenericArrayType || type instanceof Class<?> && ((Class<?>) type).isArray();
+ return true;
}
/**
- * Gets the array component type of {@code type}.
+ * Tests if the given value can be assigned to the target type
+ * following the Java generics rules.
*
- * @param type the type to be checked
- * @return component type or null if type is not an array type
+ * @param value the value to be checked
+ * @param type the target type
+ * @return {@code true} if {@code value} is an instance of {@code type}.
*/
- public static Type getArrayComponentType(final Type type) {
- if (type instanceof Class<?>) {
- final Class<?> cls = (Class<?>) type;
- return cls.isArray() ? cls.getComponentType() : null;
- }
- if (type instanceof GenericArrayType) {
- return ((GenericArrayType) type).getGenericComponentType();
+ public static boolean isInstance(final Object value, final Type type) {
+ if (type == null) {
+ return false;
}
- return null;
+
+ return value == null ? !(type instanceof Class<?>) || !((Class<?>) type).isPrimitive()
+ : isAssignable(value.getClass(), type, null);
}
/**
- * Gets a type representing {@code type} with variable assignments "unrolled."
+ * Maps type variables.
*
- * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, Class)}
- * @param type the type to unroll variable assignments for
- * @return Type
- * @since 3.2
+ * @param <T> the generic type of the class in question
+ * @param cls the class in question
+ * @param parameterizedType the parameterized type
+ * @param typeVarAssigns the map to be filled
*/
- public static Type unrollVariables(Map<TypeVariable<?>, Type> typeArguments, final Type type) {
- if (typeArguments == null) {
- typeArguments = Collections.emptyMap();
- }
- if (containsTypeVariables(type)) {
- if (type instanceof TypeVariable<?>) {
- return unrollVariables(typeArguments, typeArguments.get(type));
- }
- if (type instanceof ParameterizedType) {
- final ParameterizedType p = (ParameterizedType) type;
- final Map<TypeVariable<?>, Type> parameterizedTypeArguments;
- if (p.getOwnerType() == null) {
- parameterizedTypeArguments = typeArguments;
- } else {
- parameterizedTypeArguments = new HashMap<>(typeArguments);
- parameterizedTypeArguments.putAll(getTypeArguments(p));
- }
- final Type[] args = p.getActualTypeArguments();
- for (int i = 0; i < args.length; i++) {
- final Type unrolled = unrollVariables(parameterizedTypeArguments, args[i]);
- if (unrolled != null) {
- args[i] = unrolled;
- }
- }
- return parameterizeWithOwner(p.getOwnerType(), (Class<?>) p.getRawType(), args);
- }
- if (type instanceof WildcardType) {
- final WildcardType wild = (WildcardType) type;
- return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild.getUpperBounds()))
- .withLowerBounds(unrollBounds(typeArguments, wild.getLowerBounds())).build();
- }
+ private static <T> void mapTypeVariablesToArguments(final Class<T> cls,
+ final ParameterizedType parameterizedType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ // capture the type variables from the owner type that have assignments
+ final Type ownerType = parameterizedType.getOwnerType();
+
+ if (ownerType instanceof ParameterizedType) {
+ // recursion to make sure the owner's owner type gets processed
+ mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns);
}
- return type;
- }
- /**
- * Unrolls variables in a type bounds array.
- *
- * @param typeArguments assignments {@link Map}
- * @param bounds in which to expand variables
- * @return {@code bounds} with any variables reassigned
- * @since 3.2
- */
- private static Type[] unrollBounds(final Map<TypeVariable<?>, Type> typeArguments, final Type[] bounds) {
- Type[] result = bounds;
- int i = 0;
- for (; i < result.length; i++) {
- final Type unrolled = unrollVariables(typeArguments, result[i]);
- if (unrolled == null) {
- result = ArrayUtils.remove(result, i--);
- } else {
- result[i] = unrolled;
+ // parameterizedType is a generic interface/class (or it's in the owner
+ // hierarchy of said interface/class) implemented/extended by the class
+ // cls. Find out which type variables of cls are type arguments of
+ // parameterizedType:
+ final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+
+ // of the cls's type variables that are arguments of parameterizedType,
+ // find out which ones can be determined from the super type's arguments
+ final TypeVariable<?>[] typeVars = getRawType(parameterizedType).getTypeParameters();
+
+ // use List view of type parameters of cls so the contains() method can be used:
+ final List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls
+ .getTypeParameters());
+
+ for (int i = 0; i < typeArgs.length; i++) {
+ final TypeVariable<?> typeVar = typeVars[i];
+ final Type typeArg = typeArgs[i];
+
+ // argument of parameterizedType is a type variable of cls
+ if (typeVarList.contains(typeArg)
+ // type variable of parameterizedType has an assignment in
+ // the super type.
+ && typeVarAssigns.containsKey(typeVar)) {
+ // map the assignment to the cls's type variable
+ typeVarAssigns.put((TypeVariable<?>) typeArg, typeVarAssigns.get(typeVar));
}
}
- return result;
}
/**
- * Tests, recursively, whether any of the type parameters associated with {@code type} are bound to variables.
+ * Strips out the redundant upper bound types in type
+ * variable types and wildcard types (or it would with wildcard types if
+ * multiple upper bounds were allowed).
+ *
+ * <p>
+ * Example, with the variable type declaration:
+ * </p>
*
- * @param type the type to check for type variables
- * @return boolean
- * @since 3.2
+ * <pre><K extends java.util.Collection<String> &
+ * java.util.List<String>></pre>
+ *
+ * <p>
+ * since {@code List} is a subinterface of {@code Collection},
+ * this method will return the bounds as if the declaration had been:
+ * </p>
+ *
+ * <pre><K extends java.util.List<String>></pre>
+ *
+ * @param bounds an array of types representing the upper bounds of either
+ * {@link WildcardType} or {@link TypeVariable}, not {@code null}.
+ * @return an array containing the values from {@code bounds} minus the
+ * redundant types.
*/
- public static boolean containsTypeVariables(final Type type) {
- if (type instanceof TypeVariable<?>) {
- return true;
- }
- if (type instanceof Class<?>) {
- return ((Class<?>) type).getTypeParameters().length > 0;
+ public static Type[] normalizeUpperBounds(final Type[] bounds) {
+ Validate.notNull(bounds, "null value specified for bounds array");
+ // don't bother if there's only one (or none) type
+ if (bounds.length < 2) {
+ return bounds;
}
- if (type instanceof ParameterizedType) {
- for (final Type arg : ((ParameterizedType) type).getActualTypeArguments()) {
- if (containsTypeVariables(arg)) {
- return true;
+
+ final Set<Type> types = new HashSet<>(bounds.length);
+
+ for (final Type type1 : bounds) {
+ boolean subtypeFound = false;
+
+ for (final Type type2 : bounds) {
+ if (type1 != type2 && isAssignable(type2, type1, null)) {
+ subtypeFound = true;
+ break;
}
}
- return false;
- }
- if (type instanceof WildcardType) {
- final WildcardType wild = (WildcardType) type;
- return containsTypeVariables(getImplicitLowerBounds(wild)[0])
- || containsTypeVariables(getImplicitUpperBounds(wild)[0]);
+
+ if (!subtypeFound) {
+ types.add(type1);
+ }
}
- return false;
+
+ return types.toArray(ArrayUtils.EMPTY_TYPE_ARRAY);
+ }
+
+ /**
+ * Creates a parameterized type instance.
+ *
+ * @param rawClass the raw class to create a parameterized type instance for
+ * @param typeArgMappings the mapping used for parameterization
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterize(final Class<?> rawClass,
+ final Map<TypeVariable<?>, Type> typeArgMappings) {
+ Validate.notNull(rawClass, "raw class is null");
+ Validate.notNull(typeArgMappings, "typeArgMappings is null");
+ return parameterizeWithOwner(null, rawClass,
+ extractTypeArgumentsFrom(typeArgMappings, rawClass.getTypeParameters()));
}
/**
@@ -1481,18 +1558,54 @@ public class TypeUtils {
}
/**
+ * Formats a {@link ParameterizedType} as a {@link String}.
+ *
+ * @param parameterizedType {@code ParameterizedType} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String parameterizedTypeToString(final ParameterizedType parameterizedType) {
+ final StringBuilder builder = new StringBuilder();
+
+ final Type useOwner = parameterizedType.getOwnerType();
+ final Class<?> raw = (Class<?>) parameterizedType.getRawType();
+
+ if (useOwner == null) {
+ builder.append(raw.getName());
+ } else {
+ if (useOwner instanceof Class<?>) {
+ builder.append(((Class<?>) useOwner).getName());
+ } else {
+ builder.append(useOwner.toString());
+ }
+ builder.append('.').append(raw.getSimpleName());
+ }
+
+ final int[] recursiveTypeIndexes = findRecursiveTypes(parameterizedType);
+
+ if (recursiveTypeIndexes.length > 0) {
+ appendRecursiveTypes(builder, recursiveTypeIndexes, parameterizedType.getActualTypeArguments());
+ } else {
+ appendAllTo(builder.append('<'), ", ", parameterizedType.getActualTypeArguments()).append('>');
+ }
+
+ return builder.toString();
+ }
+
+ /**
* Creates a parameterized type instance.
*
+ * @param owner the owning type
* @param rawClass the raw class to create a parameterized type instance for
* @param typeArgMappings the mapping used for parameterization
* @return {@link ParameterizedType}
* @since 3.2
*/
- public static final ParameterizedType parameterize(final Class<?> rawClass,
+ public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class<?> rawClass,
final Map<TypeVariable<?>, Type> typeArgMappings) {
Validate.notNull(rawClass, "raw class is null");
Validate.notNull(typeArgMappings, "typeArgMappings is null");
- return parameterizeWithOwner(null, rawClass,
+ return parameterizeWithOwner(owner, rawClass,
extractTypeArgumentsFrom(typeArgMappings, rawClass.getTypeParameters()));
}
@@ -1529,179 +1642,24 @@ public class TypeUtils {
}
/**
- * Creates a parameterized type instance.
- *
- * @param owner the owning type
- * @param rawClass the raw class to create a parameterized type instance for
- * @param typeArgMappings the mapping used for parameterization
- * @return {@link ParameterizedType}
- * @since 3.2
- */
- public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class<?> rawClass,
- final Map<TypeVariable<?>, Type> typeArgMappings) {
- Validate.notNull(rawClass, "raw class is null");
- Validate.notNull(typeArgMappings, "typeArgMappings is null");
- return parameterizeWithOwner(owner, rawClass,
- extractTypeArgumentsFrom(typeArgMappings, rawClass.getTypeParameters()));
- }
-
- /**
- * Helper method to establish the formal parameters for a parameterized type.
- *
- * @param mappings map containing the assignments
- * @param variables expected map keys
- * @return array of map values corresponding to specified keys
- */
- private static Type[] extractTypeArgumentsFrom(final Map<TypeVariable<?>, Type> mappings, final TypeVariable<?>[] variables) {
- final Type[] result = new Type[variables.length];
- int index = 0;
- for (final TypeVariable<?> var : variables) {
- Validate.isTrue(mappings.containsKey(var), "missing argument mapping for %s", toString(var));
- result[index++] = mappings.get(var);
- }
- return result;
- }
-
- /**
- * Gets a {@link WildcardTypeBuilder}.
- *
- * @return {@link WildcardTypeBuilder}
- * @since 3.2
- */
- public static WildcardTypeBuilder wildcardType() {
- return new WildcardTypeBuilder();
- }
-
- /**
- * Creates a generic array type instance.
- *
- * @param componentType the type of the elements of the array. For example the component type of {@code boolean[]}
- * is {@code boolean}
- * @return {@link GenericArrayType}
- * @since 3.2
- */
- public static GenericArrayType genericArrayType(final Type componentType) {
- return new GenericArrayTypeImpl(Validate.notNull(componentType, "componentType is null"));
- }
-
- /**
- * Tests equality of types.
- *
- * @param type1 the first type
- * @param type2 the second type
- * @return boolean
- * @since 3.2
- */
- public static boolean equals(final Type type1, final Type type2) {
- if (Objects.equals(type1, type2)) {
- return true;
- }
- if (type1 instanceof ParameterizedType) {
- return equals((ParameterizedType) type1, type2);
- }
- if (type1 instanceof GenericArrayType) {
- return equals((GenericArrayType) type1, type2);
- }
- if (type1 instanceof WildcardType) {
- return equals((WildcardType) type1, type2);
- }
- return false;
- }
-
- /**
- * Tests whether {@code t} equals {@code p}.
- *
- * @param parameterizedType LHS
- * @param type RHS
- * @return boolean
- * @since 3.2
- */
- private static boolean equals(final ParameterizedType parameterizedType, final Type type) {
- if (type instanceof ParameterizedType) {
- final ParameterizedType other = (ParameterizedType) type;
- if (equals(parameterizedType.getRawType(), other.getRawType())
- && equals(parameterizedType.getOwnerType(), other.getOwnerType())) {
- return equals(parameterizedType.getActualTypeArguments(), other.getActualTypeArguments());
- }
- }
- return false;
- }
-
- /**
- * Tests whether {@code t} equals {@code a}.
- *
- * @param genericArrayType LHS
- * @param type RHS
- * @return boolean
- * @since 3.2
- */
- private static boolean equals(final GenericArrayType genericArrayType, final Type type) {
- return type instanceof GenericArrayType
- && equals(genericArrayType.getGenericComponentType(), ((GenericArrayType) type).getGenericComponentType());
- }
-
- /**
- * Tests whether {@code t} equals {@code w}.
+ * Finds the mapping for {@code type} in {@code typeVarAssigns}.
*
- * @param wildcardType LHS
- * @param type RHS
- * @return boolean
- * @since 3.2
+ * @param type the type to be replaced
+ * @param typeVarAssigns the map with type variables
+ * @return the replaced type
+ * @throws IllegalArgumentException if the type cannot be substituted
*/
- private static boolean equals(final WildcardType wildcardType, final Type type) {
- if (type instanceof WildcardType) {
- final WildcardType other = (WildcardType) type;
- return equals(getImplicitLowerBounds(wildcardType), getImplicitLowerBounds(other))
- && equals(getImplicitUpperBounds(wildcardType), getImplicitUpperBounds(other));
- }
- return false;
- }
+ private static Type substituteTypeVariables(final Type type, final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ if (type instanceof TypeVariable<?> && typeVarAssigns != null) {
+ final Type replacementType = typeVarAssigns.get(type);
- /**
- * Tests whether {@code t1} equals {@code t2}.
- *
- * @param type1 LHS
- * @param type2 RHS
- * @return boolean
- * @since 3.2
- */
- private static boolean equals(final Type[] type1, final Type[] type2) {
- if (type1.length == type2.length) {
- for (int i = 0; i < type1.length; i++) {
- if (!equals(type1[i], type2[i])) {
- return false;
- }
+ if (replacementType == null) {
+ throw new IllegalArgumentException("missing assignment type for type variable "
+ + type);
}
- return true;
- }
- return false;
- }
-
- /**
- * Formats a given type as a Java-esque String.
- *
- * @param type the type to create a String representation for, not {@code null}
- * @return String
- * @since 3.2
- */
- public static String toString(final Type type) {
- Validate.notNull(type);
- if (type instanceof Class<?>) {
- return classToString((Class<?>) type);
- }
- if (type instanceof ParameterizedType) {
- return parameterizedTypeToString((ParameterizedType) type);
- }
- if (type instanceof WildcardType) {
- return wildcardTypeToString((WildcardType) type);
- }
- if (type instanceof TypeVariable<?>) {
- return typeVariableToString((TypeVariable<?>) type);
- }
- if (type instanceof GenericArrayType) {
- return genericArrayTypeToString((GenericArrayType) type);
+ return replacementType;
}
- throw new IllegalArgumentException(ObjectUtils.identityToString(type));
+ return type;
}
/**
@@ -1725,63 +1683,74 @@ public class TypeUtils {
buf.insert(0, c.getSimpleName()).insert(0, '.');
c = c.getEnclosingClass();
}
- } else if (d instanceof Type) {// not possible as of now
- buf.append(toString((Type) d));
- } else {
- buf.append(d);
- }
- return buf.append(':').append(typeVariableToString(var)).toString();
- }
-
- /**
- * Wraps the specified {@link Type} in a {@link Typed} wrapper.
- *
- * @param <T> inferred generic type
- * @param type to wrap
- * @return Typed<T>
- * @since 3.2
- */
- public static <T> Typed<T> wrap(final Type type) {
- return () -> type;
+ } else if (d instanceof Type) {// not possible as of now
+ buf.append(toString((Type) d));
+ } else {
+ buf.append(d);
+ }
+ return buf.append(':').append(typeVariableToString(var)).toString();
}
- /**
- * Wraps the specified {@link Class} in a {@link Typed} wrapper.
- *
- * @param <T> generic type
- * @param type to wrap
- * @return Typed<T>
- * @since 3.2
- */
- public static <T> Typed<T> wrap(final Class<T> type) {
- return wrap((Type) type);
+ private static <T> String toString(final T object) {
+ return object instanceof Type ? toString((Type) object) : object.toString();
}
/**
- * Formats a {@link Class} as a {@link String}.
+ * Formats a given type as a Java-esque String.
*
- * @param cls {@code Class} to format
+ * @param type the type to create a String representation for, not {@code null}
* @return String
* @since 3.2
*/
- private static String classToString(final Class<?> cls) {
- if (cls.isArray()) {
- return toString(cls.getComponentType()) + "[]";
+ public static String toString(final Type type) {
+ Validate.notNull(type);
+ if (type instanceof Class<?>) {
+ return classToString((Class<?>) type);
+ }
+ if (type instanceof ParameterizedType) {
+ return parameterizedTypeToString((ParameterizedType) type);
}
+ if (type instanceof WildcardType) {
+ return wildcardTypeToString((WildcardType) type);
+ }
+ if (type instanceof TypeVariable<?>) {
+ return typeVariableToString((TypeVariable<?>) type);
+ }
+ if (type instanceof GenericArrayType) {
+ return genericArrayTypeToString((GenericArrayType) type);
+ }
+ throw new IllegalArgumentException(ObjectUtils.identityToString(type));
+ }
- final StringBuilder buf = new StringBuilder();
+ /**
+ * Determines whether or not specified types satisfy the bounds of their
+ * mapped type variables. When a type parameter extends another (such as
+ * {@code <T, S extends T>}), uses another as a type parameter (such as
+ * {@code <T, S extends Comparable>>}), or otherwise depends on
+ * another type variable to be specified, the dependencies must be included
+ * in {@code typeVarAssigns}.
+ *
+ * @param typeVarAssigns specifies the potential types to be assigned to the
+ * type variables, not {@code null}.
+ * @return whether or not the types can be assigned to their respective type
+ * variables.
+ */
+ public static boolean typesSatisfyVariables(final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ Validate.notNull(typeVarAssigns, "typeVarAssigns is null");
+ // all types must be assignable to all the bounds of their mapped
+ // type variable.
+ for (final Map.Entry<TypeVariable<?>, Type> entry : typeVarAssigns.entrySet()) {
+ final TypeVariable<?> typeVar = entry.getKey();
+ final Type type = entry.getValue();
- if (cls.getEnclosingClass() != null) {
- buf.append(classToString(cls.getEnclosingClass())).append('.').append(cls.getSimpleName());
- } else {
- buf.append(cls.getName());
- }
- if (cls.getTypeParameters().length > 0) {
- buf.append('<');
- appendAllTo(buf, ", ", cls.getTypeParameters());
- buf.append('>');
+ for (final Type bound : getImplicitBounds(typeVar)) {
+ if (!isAssignable(type, substituteTypeVariables(bound, typeVarAssigns),
+ typeVarAssigns)) {
+ return false;
+ }
+ }
}
- return buf.toString();
+ return true;
}
/**
@@ -1802,71 +1771,101 @@ public class TypeUtils {
}
/**
- * Formats a {@link ParameterizedType} as a {@link String}.
+ * Unrolls variables in a type bounds array.
*
- * @param parameterizedType {@code ParameterizedType} to format
- * @return String
+ * @param typeArguments assignments {@link Map}
+ * @param bounds in which to expand variables
+ * @return {@code bounds} with any variables reassigned
* @since 3.2
*/
- private static String parameterizedTypeToString(final ParameterizedType parameterizedType) {
- final StringBuilder builder = new StringBuilder();
-
- final Type useOwner = parameterizedType.getOwnerType();
- final Class<?> raw = (Class<?>) parameterizedType.getRawType();
-
- if (useOwner == null) {
- builder.append(raw.getName());
- } else {
- if (useOwner instanceof Class<?>) {
- builder.append(((Class<?>) useOwner).getName());
+ private static Type[] unrollBounds(final Map<TypeVariable<?>, Type> typeArguments, final Type[] bounds) {
+ Type[] result = bounds;
+ int i = 0;
+ for (; i < result.length; i++) {
+ final Type unrolled = unrollVariables(typeArguments, result[i]);
+ if (unrolled == null) {
+ result = ArrayUtils.remove(result, i--);
} else {
- builder.append(useOwner.toString());
+ result[i] = unrolled;
}
- builder.append('.').append(raw.getSimpleName());
- }
-
- final int[] recursiveTypeIndexes = findRecursiveTypes(parameterizedType);
-
- if (recursiveTypeIndexes.length > 0) {
- appendRecursiveTypes(builder, recursiveTypeIndexes, parameterizedType.getActualTypeArguments());
- } else {
- appendAllTo(builder.append('<'), ", ", parameterizedType.getActualTypeArguments()).append('>');
}
-
- return builder.toString();
+ return result;
}
- private static void appendRecursiveTypes(final StringBuilder builder, final int[] recursiveTypeIndexes,
- final Type[] argumentTypes) {
- for (int i = 0; i < recursiveTypeIndexes.length; i++) {
- appendAllTo(builder.append('<'), ", ", argumentTypes[i].toString()).append('>');
- }
-
- final Type[] argumentsFiltered = ArrayUtils.removeAll(argumentTypes, recursiveTypeIndexes);
-
- if (argumentsFiltered.length > 0) {
- appendAllTo(builder.append('<'), ", ", argumentsFiltered).append('>');
- }
+ /**
+ * Look up {@code var} in {@code typeVarAssigns} <em>transitively</em>,
+ * i.e. keep looking until the value found is <em>not</em> a type variable.
+ *
+ * @param typeVariable the type variable to look up
+ * @param typeVarAssigns the map used for the look up
+ * @return Type or {@code null} if some variable was not in the map
+ * @since 3.2
+ */
+ private static Type unrollVariableAssignments(TypeVariable<?> typeVariable,
+ final Map<TypeVariable<?>, Type> typeVarAssigns) {
+ Type result;
+ do {
+ result = typeVarAssigns.get(typeVariable);
+ if (result instanceof TypeVariable<?> && !result.equals(typeVariable)) {
+ typeVariable = (TypeVariable<?>) result;
+ continue;
+ }
+ break;
+ } while (true);
+ return result;
}
- private static int[] findRecursiveTypes(final ParameterizedType parameterizedType) {
- final Type[] filteredArgumentTypes = Arrays.copyOf(parameterizedType.getActualTypeArguments(),
- parameterizedType.getActualTypeArguments().length);
- int[] indexesToRemove = {};
- for (int i = 0; i < filteredArgumentTypes.length; i++) {
- if (filteredArgumentTypes[i] instanceof TypeVariable<?>) {
- if (containsVariableTypeSameParametrizedTypeBound(((TypeVariable<?>) filteredArgumentTypes[i]),
- parameterizedType)) {
- indexesToRemove = ArrayUtils.add(indexesToRemove, i);
+ /**
+ * Gets a type representing {@code type} with variable assignments "unrolled."
+ *
+ * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, Class)}
+ * @param type the type to unroll variable assignments for
+ * @return Type
+ * @since 3.2
+ */
+ public static Type unrollVariables(Map<TypeVariable<?>, Type> typeArguments, final Type type) {
+ if (typeArguments == null) {
+ typeArguments = Collections.emptyMap();
+ }
+ if (containsTypeVariables(type)) {
+ if (type instanceof TypeVariable<?>) {
+ return unrollVariables(typeArguments, typeArguments.get(type));
+ }
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType p = (ParameterizedType) type;
+ final Map<TypeVariable<?>, Type> parameterizedTypeArguments;
+ if (p.getOwnerType() == null) {
+ parameterizedTypeArguments = typeArguments;
+ } else {
+ parameterizedTypeArguments = new HashMap<>(typeArguments);
+ parameterizedTypeArguments.putAll(getTypeArguments(p));
+ }
+ final Type[] args = p.getActualTypeArguments();
+ for (int i = 0; i < args.length; i++) {
+ final Type unrolled = unrollVariables(parameterizedTypeArguments, args[i]);
+ if (unrolled != null) {
+ args[i] = unrolled;
+ }
}
+ return parameterizeWithOwner(p.getOwnerType(), (Class<?>) p.getRawType(), args);
+ }
+ if (type instanceof WildcardType) {
+ final WildcardType wild = (WildcardType) type;
+ return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild.getUpperBounds()))
+ .withLowerBounds(unrollBounds(typeArguments, wild.getLowerBounds())).build();
}
}
- return indexesToRemove;
+ return type;
}
- private static boolean containsVariableTypeSameParametrizedTypeBound(final TypeVariable<?> typeVariable,
- final ParameterizedType parameterizedType) {
- return ArrayUtils.contains(typeVariable.getBounds(), parameterizedType);
+ /**
+ * Gets a {@link WildcardTypeBuilder}.
+ *
+ * @return {@link WildcardTypeBuilder}
+ * @since 3.2
+ */
+ public static WildcardTypeBuilder wildcardType() {
+ return new WildcardTypeBuilder();
}
/**
@@ -1889,39 +1888,40 @@ public class TypeUtils {
}
/**
- * Formats a {@link GenericArrayType} as a {@link String}.
+ * Wraps the specified {@link Class} in a {@link Typed} wrapper.
*
- * @param genericArrayType {@code GenericArrayType} to format
- * @return String
+ * @param <T> generic type
+ * @param type to wrap
+ * @return Typed<T>
* @since 3.2
*/
- private static String genericArrayTypeToString(final GenericArrayType genericArrayType) {
- return String.format("%s[]", toString(genericArrayType.getGenericComponentType()));
+ public static <T> Typed<T> wrap(final Class<T> type) {
+ return wrap((Type) type);
}
/**
- * Appends {@code types} to {@code builder} with separator {@code sep}.
+ * Wraps the specified {@link Type} in a {@link Typed} wrapper.
*
- * @param builder destination
- * @param sep separator
- * @param types to append
- * @return {@code builder}
+ * @param <T> inferred generic type
+ * @param type to wrap
+ * @return Typed<T>
* @since 3.2
*/
- private static <T> StringBuilder appendAllTo(final StringBuilder builder, final String sep,
- @SuppressWarnings("unchecked") final T... types) {
- Validate.notEmpty(Validate.noNullElements(types));
- if (types.length > 0) {
- builder.append(toString(types[0]));
- for (int i = 1; i < types.length; i++) {
- builder.append(sep).append(toString(types[i]));
- }
- }
- return builder;
+ public static <T> Typed<T> wrap(final Type type) {
+ return () -> type;
}
- private static <T> String toString(final T object) {
- return object instanceof Type ? toString((Type) object) : object.toString();
+ /**
+ * {@code TypeUtils} instances should NOT be constructed in standard
+ * programming. Instead, the class should be used as
+ * {@code TypeUtils.isAssignable(cls, toClass)}.
+ * <p>
+ * This constructor is public to permit tools that require a JavaBean instance
+ * to operate.
+ * </p>
+ */
+ public TypeUtils() {
+ super();
}
}