java泛型擦除
前文介绍
java中的泛型实际上是伪泛型,因为在编译过程中,所有的泛型信息都会被擦除,这里涉及到一个概念——类型擦除(java的泛型基本上是在编译器生成字节码时,使用泛型的时候加上类型参数,整个过程就是类型的擦除)。泛型可以理解为将数据类型作为参数的一种手段。
通过例子来理解泛型擦除
介绍到这里,我们开始整活。如何在一个list中存一个Integer 一个 String呢?我们使用反射机制,可以实现。
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
public class IntegerAndStringInList {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Integer> list = new ArrayList<>();
list.add(666);
//通过反射来存string
try {
list.getClass().getMethod("add", Object.class).invoke(list, "yokna");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(int i = 0;i < list.size();i++) {
System.out.println(list.get(i));
}
}
}
运行结果如下:
剖析原因
首先ArrayList在编译时会经历类型擦除,擦除后的类型为ArrayList,这样没有了类型的约束,就可以存下String类型。在擦除过程中,integer被object所代替,object又是所有类的父类,我们称之为原始类型,再通过反射就可以存储字符串了。
验证
这样的话,我们就可以把代码修改成
public static void main(String[] args) {
// TODO Auto-generated method stub
//此处去掉泛型约束
ArrayList list = new ArrayList();
list.add(666);
list.add("yokna");
for(int i = 0;i < list.size();i++) {
System.out.println(list.get(i));
}
}
去掉泛型约束后,更容易就实现了一个list中存多种数据类型。
事实上,如果不指定泛型,list可以存取任意的对象。这正好验证了我们前面的泛型擦除理论。
为什么要设计泛型呢?
在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是本身就是一个安全隐患。泛型是为了解决类型转换问题,java编译器先检查代码中泛型的类型,然后再进行类型擦除,再进行编译,这样就避免出现类型膨胀问题。
tips:
instanceof关键字,用来判断某对象是否是某个特定类或者是该特定类的子类的一个实例。
用法:
result = object instanceof class
参数:
Result:布尔类型。
Object:必选项。任意对象表达式。
Class:必选项。任意已定义的对象类。
在泛型类型被擦除后,instanceof返回的结果是false;
例如:
ArrayList<String> arrayList = new ArrayList<>();
if(arrayList instanceof ArrayList<String>){
System.out.println("竟然没有发生类型擦除!")
}else{
System.out.println("类型擦除确实发生了!")
}
莫听穿林打叶声,何妨吟啸且徐行。
竹杖芒鞋轻胜马,谁怕?
一蓑烟雨任平生。
料峭春风吹酒醒,微冷,山头斜照却相迎。
回首向来萧瑟处,归去,也无风雨也无芩。