要生成类的泛型类型参数的实例,先要生成类的泛型类型参数的Class对象。比如,对于泛型类Testable<T, S>,它的T、S这两个类型参数的Class对象:Class<T>, Class<S>。
public class Testable<T, S> {
private Class<T> tClazz;
private Class<S> sClazz;
public Testable() {
}
public Class<T> getTClazz() {
return tClazz;
}
public Class<S> getSClazz() {
return sClazz;
}
}
网上可以找到的一般性的处理方法如下:
public static List<Class<?>> getTypeArgumentClassList(Class<?> genericClass) {
Type rawType = genericClass.getGenericSuperclass();
ParameterizedType o = (ParameterizedType) rawType;
Type[] typeArguments = o.getActualTypeArguments();
List<Class<?>> typeArgumentClassList = new ArrayList<Class<?>>();
for (Type t : typeArguments) {
Class<?> clazz = (Class<?>) t;
typeArgumentClassList.add(clazz);
}
return typeArgumentClassList;
}
测试代码:
public static void doTest() {
Testable<String, Integer> g = new Testable<String, Integer>();
for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
}
}
运行后报错:
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
原因是Class.getGenericSuperclass()是去获取泛型父类的Type对象,而Testable<T, S>没有泛型父类,所以报错了。由于JDK中没有类似于Class.getGenericClass()这样的方法,所以只能想别的办法解决这个问题:
Testable<String, Integer> g = new Testable<String, Integer>() {};
加上{}后,这行代码的意思是创建Testable<String, Integer>()匿名子类,然后创建这个匿名子类的对象,这样对这个匿名子类调用Class.getGenericSuperclass()就不会有问题,因为它的泛型父类就是Testable<T, S>。
public static void doTest() {
Testable<String, Integer> g = new Testable<String, Integer>(){};
for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
}
}
代码运行结果:
class java.lang.String : java.lang.String
class java.lang.Integer : java.lang.Integer
如果测试代码换成:
public static void doTest() {
Testable<String, List<?>> g = new Testable<String, List<?>>() {
};
for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
}
}
代码报错:
Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class
此报错说明,List<?>对应的Type对象实际上是java.lang.reflect.ParameterizedType,不能直接转化为Class<?>,因此,修改代码如下:
public static List<Class<?>> getTypeArgumentClassList(Class<?> genericClass) {
Type rawType = genericClass.getGenericSuperclass();
ParameterizedType o = (ParameterizedType) rawType;
Type[] typeArguments = o.getActualTypeArguments();
List<Class<?>> typeArgumentClassList = new ArrayList<Class<?>>();
for (Type t : typeArguments) {
Class<?> clazz = null;
if (t instanceof Class<?>) {
clazz = (Class<?>) t;
} else {
Type innerType = ((ParameterizedType) t).getRawType();
clazz = (Class<?>) innerType;
}
typeArgumentClassList.add(clazz);
}
return typeArgumentClassList;
}
运行结果:
class java.lang.String : java.lang.String
interface java.util.List : java.util.List
如果测试代码换成:
public static void doTest() {
Testable<String, List<?>[]> g = new Testable<String, List<?>[]>() {
};
for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
}
}
代码报错:
Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl cannot be cast to java.lang.reflect.ParameterizedType
此报错说明,List<?>[]对应的Type对象实际上是java.lang.reflect.GenericArrayType不是java.lang.reflect.ParameterizedType,因此,修改代码如下:
public static List<Class<?>> getTypeArgumentClassList(Class<?> genericClass) {
Type rawType = genericClass.getGenericSuperclass();
ParameterizedType o = (ParameterizedType) rawType;
Type[] typeArguments = o.getActualTypeArguments();
List<Class<?>> typeArgumentClassList = new ArrayList<Class<?>>();
for (Type t : typeArguments) {
Class<?> clazz = null;
if (t instanceof Class<?>) {
clazz = (Class<?>) t;
} else if (t instanceof ParameterizedType) {
Type innerType = ((ParameterizedType) t).getRawType();
clazz = (Class<?>) innerType;
} else if (t instanceof GenericArrayType) {
Type compType = ((GenericArrayType) t).getGenericComponentType();
Type innerType = ((ParameterizedType) compType).getRawType();
Class<?> compClazz = (Class<?>) innerType;
clazz = Array.newInstance(compClazz, 0).getClass();
}
typeArgumentClassList.add(clazz);
}
return typeArgumentClassList;
}
运行结果:
class java.lang.String : java.lang.String
class [Ljava.util.List; : java.util.List[]
由于List<?>[]是一个数组,它的Class对象的获取可能通过:
Array.newInstance(compClazz, 0).getClass();
如果测试代码换成:
public static void doTest() {
Testable<String, List<String>[][]> g = new Testable<String, List<String>[][]>() {
};
for (Class<?> typeArgumentClazz : getTypeArgumentClassList(g.getClass())) {
System.out.println(typeArgumentClazz + " : \t" + typeArgumentClazz.getCanonicalName());
}
}
代码报错:
Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl cannot be cast to java.lang.reflect.ParameterizedType
此报错的原因是,compType实现上是List<String>[]对应的Type对象,它还是java.lang.reflect.GenericArrayType不是java.lang.reflect.ParameterizedType,因而还需要再做一次((GenericArrayType) t).getGenericComponentType();
但如果是Testable<String, List<String>[][][]>、Testable<String, List<String>[][][][]>、.......,那就不能解决了。对以上代码重构后,提供完整实现如下:
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public final class GenericTools {
public static List<Class<?>> getTypeArgumentClassList(Class<?> genericClass) {
Type rawType = genericClass.getGenericSuperclass();
if (rawType instanceof Class<?>) {
return Collections.emptyList();
}
ParameterizedType o = (ParameterizedType) rawType;
Type[] typeArguments = o.getActualTypeArguments();
List<Class<?>> typeArgumentClassList = new ArrayList<Class<?>>();
for (Type typeArgument : typeArguments) {
typeArgumentClassList.add(getTypeArgumentClass(typeArgument));
}
return typeArgumentClassList;
}
public static Class<?> getTypeArgumentClass(Type typeArgument) {
if (typeArgument instanceof Class<?>) {
return (Class<?>) typeArgument;
} else if (typeArgument instanceof ParameterizedType) {
Type innerType = ((ParameterizedType) typeArgument).getRawType();
return getTypeArgumentClass(innerType);
} else if (typeArgument instanceof GenericArrayType) {
Type compType = ((GenericArrayType) typeArgument).getGenericComponentType();
Class<?> compClazz = getTypeArgumentClass(compType);
if (compClazz != null) {
return Array.newInstance(compClazz, 0).getClass();
}
}
return null;
}
}
运行结果:
class java.lang.String : java.lang.String
class [[Ljava.util.List; : java.util.List[][]
最终,对Testable<T, S>修改如下:
import java.util.List;
public class Testable<T, S> {
private Class<T> tClazz;
private Class<S> sClazz;
@SuppressWarnings("unchecked")
public Testable() {
List<Class<?>> clazzList = GenericTools.getTypeArgumentClassList(this.getClass());
tClazz = (Class<T>) clazzList.get(0);
sClazz = (Class<S>) clazzList.get(1);
}
public Class<T> getTClazz() {
return tClazz;
}
public Class<S> getSClazz() {
return sClazz;
}
}
这样就可以获取到T, S两个泛型类型的Class对象,为后续对这些泛型参数实例化提供了条件。
参考文档
Java Reflection GenericArrayType Example
Java Reflection GenericArrayType Example - Java Articles
How to create a generic array?
java - How to create a generic array? - Stack Overflow
How to create a generic array in Java?
How to create a generic array in Java? - Stack Overflow