众所周知,Java中的泛型在编译期被擦除,那有没有办法在运行时获取到泛型的原始类型呢?有的。
获取泛型类型
如果定义一个类X,这个类继承自某泛型类,并给泛型提供一个具体的类型,那这个类X就会在类型定义中保留泛型类型。
直接上代码:
Object list = new ArrayList<String>() {
};
Type genericSuper = list.getClass().getGenericSuperclass();
if (genericSuper instanceof ParameterizedType) {
Type[] types = ((ParameterizedType) genericSuper).getActualTypeArguments();
for (Type t : types) {
System.out.println(t);
}
}
// 输出
> class java.lang.String
首先这里定义了ArrayList的一个匿名子类,然后通过反射,就可以获取到泛型的具体类型了。
这里的子类化很重要。看一下如果把第一行的子类化去掉会怎样?
Object list = new ArrayList<String>();
// 其他不变
输出:
> E
这个E是ArrayList定义里面的E,如果我们使用HashMap测试,就会出现下面的结果:
Object list = new HashMap<String, String>();
// 其他不变
// 输出
> K
> V
说明 getActualTypeArguments 拿到的是类型定义时,给泛型提供的名称,如果提供的是具体的类型,则为类型的完整名称。
强制转换
运行时通过类型强制转换的成功与否,能否确定泛型类型?答案是不能。
看下面代码。显示创建了一个ArrayList<String>对象,但是把对象赋值给了一个Object类型。此时编译期类型擦除已生效,后续可以对obj对象随意的搓圆压扁(转换为泛型为任何类型的ArrayList类型),没有任何异常。当然如果对里面的内容进行错误的读写,就会遭遇类型转换异常的大棒。
Object obj = new ArrayList<String>();
List<Integer> intList = (List<Integer>) obj; // 成功
List<String> strList = (List<String>) obj; // 成功
如果保留obj的类型会怎样?
List<String> obj = new ArrayList<String>();
List<String> strList = (List<String>) obj;// 成功
List<Integer> intList = (List<Integer>) obj; // 编译失败
很明显,类型在上面摆着,这样的转换在编译期就无法通过,因为这时还没有进行“类型擦除”。