一、泛型擦除概念
泛型擦除的意思就是说:我们平时在代码中显示声明的泛型在编译成功后其实都丢失了泛型的约束,也就是泛型擦除。比如:声明了一个ArrayList<String> list = new ArrayList();
在JVM层面只会看到一个Arraylist list = new ArrayList();
丢失了泛型String
二、泛型擦除的体现
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
ArrayList<Date> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass());
}
上面代码输出结果为true,这说明即使这两个list的泛型不同,但是都是属于同一个ArrayList.class
三、利用泛型擦除的缺陷突破泛型限制
根据一中概念可以知道在编译期间将泛型信息擦除掉了,那么利用Java反射允许在运行期间修改字节码文件的特点,可以突破泛型限制。
在下面的例子中,在一个本身为ArrayList<String> list = new ArrayList<>();
的集合中加入了一个Date的对象,结果正确插入了数据并且没有出现错误。
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
public class Test {
// 成员变量,指定泛型为String
private ArrayList<String> list = new ArrayList<>();
public void printList() throws NoSuchFieldException, IllegalAccessException {
Field field = this.getClass().getDeclaredField("list");
//取消对private类型成员变量的检查(当前list为private类型)
field.setAccessible(true);
//获取本类的字段
ArrayList arrayList = (ArrayList) field.get(this);
//为list添加String类型元素
arrayList.add("red");
//为list添加Date类型元素
arrayList.add(new Date());
System.out.println(arrayList);
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Test apple = new Test();
apple.printList();
}
}
// 输出结果为 [red, Mon Jul 26 17:28:16 CST 2021]
输出结果为 [red, Mon Jul 26 17:28:16 CST 2021],根据这个输出结果可以看出反射机制可以利用泛型擦除的弱点,在运行期间插入非泛型类型的数据。
四、泛型为什么不能为基本数据类型?
Java中类的泛型会在编译期间经过泛型擦除的过程,当泛型类型被擦除后,我们显示声明的泛型类型就相当于失效了,同时退化成为默认类型也就是Object类型,然后Object类型是一个类,如int、double等这种基本类型并不是引用类型,其父类不是Object(它们本身也没有父类),因此泛型不能为基本数据类型。