要生成類的泛型類型參數的執行個體,先要生成類的泛型類型參數的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
https://www.javarticles.com/2016/02/java-reflection-genericarraytype-example.html
How to create a generic array?
https://stackoverflow.com/questions/18581002/how-to-create-a-generic-array
How to create a generic array in Java?
https://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java