最近在优化JDBFly代码时,需要通过泛型获得实际的实体对象类型,封装了一个工具类,分享给大家,目前该工具类实现了通过子类获取父类或接口指定位置的泛型参数对应的实际参数,可以通过泛型参数名称或位置进行查找,以下为工具类代码:
package com.jianggujin.util;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
/**
* 泛型参数工具
*
* @author jianggujin
*
*/
public class TypeParameterUtils {
/**
* 查找真实的类型参数
*
* @param thisClass
* @param parameterizedSuperclass
* @param typeParamIndex
* @return
*/
public static Class<?> findTypeParameter(Class<?> thisClass, Class<?> parameterizedSuperclass, int typeParamIndex) {
TypeVariable<?>[] typeParams = parameterizedSuperclass.getTypeParameters();
String typeParamName = null;
if (typeParamIndex > -1 && typeParamIndex < typeParams.length) {
typeParamName = typeParams[typeParamIndex].getName();
}
// 未找到对应泛型参数
if (typeParamName == null) {
throw new IllegalStateException(
"unknown type parameter position'" + typeParamIndex + "': " + parameterizedSuperclass);
}
return findTypeParameter(thisClass, thisClass, parameterizedSuperclass, typeParamName, typeParamIndex);
}
/**
* 查找真实的类型参数
*
* @param thisClass
* @param parameterizedSuperclass
* @param typeParamName
* @return
*/
public static Class<?> findTypeParameter(Class<?> thisClass, Class<?> parameterizedSuperclass,
String typeParamName) {
int typeParamIndex = -1;
TypeVariable<?>[] typeParams = parameterizedSuperclass.getTypeParameters();
for (int i = 0; i < typeParams.length; i++) {
if (typeParamName.equals(typeParams[i].getName())) {
typeParamIndex = i;
break;
}
}
// 未找到对应泛型参数
if (typeParamIndex < 0) {
throw new IllegalStateException(
"unknown type parameter '" + typeParamName + "': " + parameterizedSuperclass);
}
return findTypeParameter(thisClass, thisClass, parameterizedSuperclass, typeParamName, typeParamIndex);
}
private static Class<?> findTypeParameter(Class<?> currentClass, Class<?> thisClass,
Class<?> parameterizedSuperclass, String typeParamName, int typeParamIndex) {
Class<?> superClass = currentClass.getSuperclass();
if (superClass != null && parameterizedSuperclass.isAssignableFrom(superClass)) {
if (superClass == parameterizedSuperclass) {
return findTypeParameter(currentClass.getGenericSuperclass(), currentClass, thisClass,
parameterizedSuperclass, typeParamName, typeParamIndex);
}
return findTypeParameter(superClass, thisClass, parameterizedSuperclass, typeParamName, typeParamIndex);
}
Class<?>[] interfaces = currentClass.getInterfaces();
for (int pos = 0; pos < interfaces.length; pos++) {
Class<?> clazz = interfaces[pos];
if (parameterizedSuperclass.isAssignableFrom(clazz)) {
if (clazz == parameterizedSuperclass) {
return findTypeParameter(currentClass.getGenericInterfaces()[pos], currentClass, thisClass,
parameterizedSuperclass, typeParamName, typeParamIndex);
}
return findTypeParameter(clazz, thisClass, parameterizedSuperclass, typeParamName, typeParamIndex);
}
}
return fail(thisClass, typeParamName);
}
private static Class<?> findTypeParameter(Type genericType, Class<?> currentClass, Class<?> thisClass,
Class<?> parameterizedSuperclass, String typeParamName, int typeParamIndex) {
if (!(genericType instanceof ParameterizedType)) {
return Object.class;
}
Type[] actualTypeParams = ((ParameterizedType) genericType).getActualTypeArguments();
Type actualTypeParam = actualTypeParams[typeParamIndex];
if (actualTypeParam instanceof ParameterizedType) {
actualTypeParam = ((ParameterizedType) actualTypeParam).getRawType();
}
if (actualTypeParam instanceof Class) {
return (Class<?>) actualTypeParam;
}
if (actualTypeParam instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) actualTypeParam).getGenericComponentType();
if (componentType instanceof ParameterizedType) {
componentType = ((ParameterizedType) componentType).getRawType();
}
if (componentType instanceof Class) {
return Array.newInstance((Class<?>) componentType, 0).getClass();
}
}
if (actualTypeParam instanceof TypeVariable) {
// Resolved type parameter points to another type parameter.
TypeVariable<?> v = (TypeVariable<?>) actualTypeParam;
currentClass = thisClass;
if (!(v.getGenericDeclaration() instanceof Class)) {
return Object.class;
}
parameterizedSuperclass = (Class<?>) v.getGenericDeclaration();
typeParamName = v.getName();
if (parameterizedSuperclass.isAssignableFrom(thisClass)) {
return findTypeParameter(currentClass, thisClass, parameterizedSuperclass, typeParamName,
typeParamIndex);
} else {
return Object.class;
}
}
return fail(thisClass, typeParamName);
}
private static Class<?> fail(Class<?> type, String typeParamName) {
throw new IllegalStateException(
"cannot determine the type of the type parameter '" + typeParamName + "': " + type);
}
}
编写一个简单的单元测试查看运行结果
package test;
import java.util.Date;
import org.junit.Test;
import com.jianggujin.util.TypeParameterUtils;
public class Test{
@Test
public void test() {
System.err.println(TypeParameterUtils.findTypeParameter(B.class, Parent.class, "T"));
System.err.println(TypeParameterUtils.findTypeParameter(StringMapper.class, Mapper.class, "T"));
System.err.println(TypeParameterUtils.findTypeParameter(M.class, Mapper.class, "T"));
System.err.println(TypeParameterUtils.findTypeParameter(K.class, Mapper.class, "T"));
System.err.println(TypeParameterUtils.findTypeParameter(B.class, Parent.class, 0));
System.err.println(TypeParameterUtils.findTypeParameter(StringMapper.class, Mapper.class, 0));
System.err.println(TypeParameterUtils.findTypeParameter(M.class, Mapper.class, 0));
System.err.println(TypeParameterUtils.findTypeParameter(K.class, Mapper.class, 0));
}
static class Parent<T, P> {
}
static class Child<O> extends Parent<O, String> {
}
static class V extends Child<String> {
}
static class B extends V {
}
static interface Mapper<T> {
}
static interface StringMapper extends Mapper<String> {
}
static interface SubMapper<T> extends Mapper<T> {
}
static class M implements SubMapper<Date> {
}
static class K extends M {
}
}
运行单元测试,会在控制台输出如下内容:
class java.lang.String
class java.lang.String
class java.util.Date
class java.util.Date
class java.lang.String
class java.lang.String
class java.util.Date
class java.util.Date