/** * 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</code>'s parameter maps (indirectly) to <code>Iterable * </code>'s parameter, it will be able to determine that based on the super type <code> * Iterable<? extends * Map<Integer,? extends Collection<?>>></code>, the parameter of <code>TreeSet</code> is <code> * ? extends Map<Integer,? extends * Collection<?>></code>. * * @param cls the class whose type parameters are to be determined * @param superType the super type from which <code>cls</code>'s type arguments are to be * determined * @return a map of the type assignments that could be determined for the type variables in each * type in the inheritance hierarchy from <code>type</code> to <code>toClass</code> inclusive. */ public static Map<TypeVariable<?>, Type> determineTypeArguments( Class<?> cls, ParameterizedType superType) { Class<?> superClass = getRawType(superType); // compatibility check if (!isAssignable(cls, superClass)) { return null; } if (cls.equals(superClass)) { return getTypeArguments(superType, superClass, null); } // get the next class in the inheritance hierarchy Type midType = getClosestParentType(cls, superClass); // can only be a class or a parameterized type if (midType instanceof Class<?>) { return determineTypeArguments((Class<?>) midType, superType); } ParameterizedType midParameterizedType = (ParameterizedType) midType; Class<?> midClass = getRawType(midParameterizedType); // get the type variables of the mid class that map to the type // arguments of the super class Map<TypeVariable<?>, Type> typeVarAssigns = determineTypeArguments(midClass, superType); // map the arguments of the mid type to the class type variables mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns); return typeVarAssigns; }
private static void mapTypeVariablesToArguments( Class paramClass, ParameterizedType paramParameterizedType, Map paramMap) { Type localType1 = paramParameterizedType.getOwnerType(); if ((localType1 instanceof ParameterizedType)) mapTypeVariablesToArguments(paramClass, (ParameterizedType) localType1, paramMap); Type[] arrayOfType = paramParameterizedType.getActualTypeArguments(); TypeVariable[] arrayOfTypeVariable = getRawType(paramParameterizedType).getTypeParameters(); List localList = Arrays.asList(paramClass.getTypeParameters()); for (int i = 0; i < arrayOfType.length; i++) { TypeVariable localTypeVariable = arrayOfTypeVariable[i]; Type localType2 = arrayOfType[i]; if ((localList.contains(localType2)) && (paramMap.containsKey(localTypeVariable))) paramMap.put((TypeVariable) localType2, paramMap.get(localTypeVariable)); } }
public static Map determineTypeArguments( Class paramClass, ParameterizedType paramParameterizedType) { Type localType; while (true) { Class localClass = getRawType(paramParameterizedType); if (!isAssignable(paramClass, localClass)) return null; if (paramClass.equals(localClass)) return getTypeArguments(paramParameterizedType, localClass, null); localType = getClosestParentType(paramClass, localClass); if (!(localType instanceof Class)) break; paramClass = (Class) localType; } ParameterizedType localParameterizedType = (ParameterizedType) localType; Map localMap = determineTypeArguments(getRawType(localParameterizedType), paramParameterizedType); mapTypeVariablesToArguments(paramClass, localParameterizedType, localMap); return localMap; }
/** * @param cls * @param parameterizedType * @param typeVarAssigns */ private static <T> void mapTypeVariablesToArguments( Class<T> cls, ParameterizedType parameterizedType, Map<TypeVariable<?>, Type> typeVarAssigns) { // capture the type variables from the owner type that have assignments Type ownerType = parameterizedType.getOwnerType(); if (ownerType instanceof ParameterizedType) { // recursion to make sure the owner's owner type gets processed mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns); } // 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: 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 TypeVariable<?>[] typeVars = getRawType(parameterizedType).getTypeParameters(); // use List view of type parameters of cls so the contains() method can be used: List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls.getTypeParameters()); for (int i = 0; i < typeArgs.length; i++) { TypeVariable<?> typeVar = typeVars[i]; 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)); } } }