![JAR search and dependency download from the Maven repository](/logo.png)
xdean.jex.util.reflect.GenericUtil Maven / Gradle / Ivy
The newest version!
package xdean.jex.util.reflect;
import static xdean.jex.util.function.Predicates.not;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import xdean.jex.util.reflect.model.GenericArrayTypeImpl;
import xdean.jex.util.reflect.model.ParameterizedTypeImpl;
import xdean.jex.util.reflect.model.WildcardTypeImpl;
public class GenericUtil {
private static final Type[] EMPTY_TYPE_ARRAY = new Type[0];
public static ParameterizedType createParameterizedType(Class> rawType, Type ownerType,
Type... actualTypeArguments) {
return new ParameterizedTypeImpl(rawType, ownerType, actualTypeArguments);
}
public static WildcardType createWildcardType(Type[] upperBounds, Type[] lowerBounds) {
return new WildcardTypeImpl(nullToEmpty(lowerBounds), nullToEmpty(upperBounds));
}
public static GenericArrayType createGenericArrayType(Type componentType) {
return new GenericArrayTypeImpl(componentType);
}
public static Map, Type> getGenericReferenceMap(Type type) {
return TypeVisitor.of(type, b -> b
.onClass(GenericUtil::getGenericReferenceMap)
.onParameterizedType(GenericUtil::getGenericReferenceMap)
.result(Collections::emptyMap));
}
public static Map, Type> getGenericReferenceMap(ParameterizedType parameterizedType) {
return TypeVisitor.of(parameterizedType.getRawType(), b -> b
.onClass(c -> {
HashMap, Type> map = new HashMap<>();
map.putAll(getGenericReferenceMap(c));
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable>[] implTypeParams = (c).getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
map.put(implTypeParams[i], actualTypeArguments[i]);
}
return map;
})
.result(Collections::emptyMap));
}
public static Map, Type> getGenericReferenceMap(Class> clz) {
HashMap, Type> map = new HashMap<>();
List allTypes = new ArrayList<>();
allTypes.add(clz.getGenericSuperclass());
allTypes.addAll(Arrays.asList(clz.getGenericInterfaces()));
allTypes.stream()
.filter(not(null))
.map(GenericUtil::getGenericReferenceMap)
.forEach(map::putAll);
return map;
}
/**
* Get the actual generic types.
* For example:
*
*
*
* class IntList extends ArrayList<Integer>{}
* getGenericType(IntList.class, List.class);// {Integer.class}
* getGenericType(IntList.class, Collection.class);// {Integer.class}
* getGenericType(Integer.class, Comparable.class);// {Integer.class}
*
*
*
* And nested situation
*
*
*
* class A<E,T>{}
* class B<E> extends A<E,Integer>{}
* class C extends B<B<Boolean>>{}
* class D<T> extends B<B<? extends T>>{}
* class E extends D<Number>{}
* getGenericType(B.class, A.class);// {E(TypeVariable), Integer.class}
* getGenericType(C.class, A.class);// {B<Boolean>(ParameterizedType), Integer.class}
* getGenericType(E.class, A.class);// {B<? extends Number>(ParameterizedType), Integer.class}
*
*
*
* @param sourceType The type to find generic type. May Class or ParameterizedType
* @param targetClass Find the actual generic type on this type.
* @return A type array. Its length equals targetClass's generic parameters' length. Its elements can be
* {@code Class, TypeVariable, ParameterizedType}.
*/
public static Type[] getGenericTypes(Type sourceType, Class> targetClass) {
TypeVariable>[] targetTypeParameters = targetClass.getTypeParameters();
if (targetTypeParameters.length == 0) {
return EMPTY_TYPE_ARRAY;
}
Map, Type> map = getGenericReferenceMap(sourceType);
// If the sourceType is Class, there may left TypeVariable.
List> leftTypeParameters = sourceType instanceof Class
? Arrays.asList(((Class>) sourceType).getTypeParameters())
: Collections.emptyList();
return Arrays.stream(targetTypeParameters)
.map(tv -> {
Type actualType = getActualType(map, tv);
// If actualType equals tv, that means it doesn't implement the targetClass
return Objects.equals(actualType, tv) && !leftTypeParameters.contains(actualType) ? null : actualType;
})
.toArray(Type[]::new);
}
/**
* Get actual type by the type map.
*
* @param map
* @param type
* @return The actual type. Return itself if it's already the most explicit type.
*/
private static Type getActualType(Map, Type> map, Type type) {
return TypeVisitor.of(type, b -> b
.onClass(c -> c)
.onTypeVariable(tv -> map.containsKey(tv) ? getActualType(map, map.get(tv)) : tv)
.onParameterizedType(pt -> TypeVisitor.of(pt.getRawType(), bb -> bb
.onClass(c -> createParameterizedType(c, pt.getOwnerType(),
Arrays.stream(pt.getActualTypeArguments()).map(t -> getActualType(map, t)).toArray(Type[]::new)))
.result()))
.onWildcardType(wt -> createWildcardType(
Arrays.stream(wt.getUpperBounds()).map(t -> getActualType(map, t)).toArray(Type[]::new),
Arrays.stream(wt.getLowerBounds()).map(t -> getActualType(map, t)).toArray(Type[]::new)))
.onGenericArrayType(gat -> createGenericArrayType(getActualType(map, gat.getGenericComponentType())))
.result(type));
}
private static Type[] nullToEmpty(Type[] upperBounds) {
return upperBounds == null ? EMPTY_TYPE_ARRAY : upperBounds;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy