1.类型擦除是什么?
虚拟机没有泛型类型对象,所有类都属于普通类。
通过对泛型类提供一个原始类型(山区类型参数后的泛型类型名),擦除类型变量。
2.类型擦除的方式?
擦除类型变量,替换为限定类型(无限定类型则替换为Object类)
无限定类型擦除:
public class Pair<T>{
private T first;
private T second;
public Pair(T first ,T second){this.first = first; this.second = second;}
public T getFirst(){return this.first;}
public T getSecond(){return this.second;}
public void setFirst(T first){this.first = first;}
public void setSecond(T second){this.second = second;}
}
类型擦除后变为
public class Pair<T>{
private T first;
private T second;
public Pair(T first ,T second){this.first = first; this.second = second;}
public T getFirst(){return this.first;}
public T getSecond(){return this.second;}
public void setFirst(T first){this.first = first;}
public void setSecond(T second){this.second = second}
}
限定类型擦除:
class Pair<T extends Manager> {
private T a;
public <T extends Manager> T getA(){return this.a}
public setA(T value){this.a = value}
}
类型擦除后变为
class Manager{
private Manager a;
public Manager getA(){return this.a}
public setA(Manager value){this.a = value}
}
3.约束与局限性
使用java泛型时修安排考虑一下限制,大多限制都是由类型擦除引起
-
基本类型不能作为泛型的类型参数
类似 Pair<double>和Pair<int>这种写法是不成立的,原因是类型擦除时后Pair为含有Object的类,而Object类是不能存储基本数据类型的。
解决方式:使用基本数据类型的包装器,如Integer、Double等 -
运行时类型检查只适用于原始类型
由于虚拟机中的对象总是有一个特定的非泛型类型,因此所有的类型查询只产生原始类型。泛型类调用getClass方法也只是返回原始类型而已。
Pair<String> a = new Pair<>("ahdf","dfgr");
Pair<Integer> b = new Pair<>(1,2);
if (a.getClass() == b.getClass()){
System.out.printf("true");
}
//true
if (a instanceof Pair<String>) ; //Error
Pair<String> p = (Pair<String>) a ; //Error
-
不能创建参数化类型的数组
数组会记住原始数据类型并对数据类型进行校验,比如Pair[]数组,可以转换为Object数组,但试图往数组中存入其他类型的数据时,就会抛出Array-StoreException的异常。
而泛型因为泛型擦除,实际是不会保存原数据标记,出现给
Pair<String>[] table = new Pair<String>[10]; //Error
数组赋值时
table[0] = new Pair<Integer>();
由于类型擦除,会导致这种类型检查机制失效。
但有一种情况可以支持泛型类型的数组,即泛型作为个数可变的参数传入方法时
public static <T> void addAll(T... ts){}
这种情况只会得到一个警告,可以通过@SafeVarargs标注方法的方式消除警告 -
不能实例化参数变量
不能使用像new T()这样的写法。因为类型擦除会将T改为Object,而这种实例化方式只会实例化出一个Object对象。
解决方式:
1)提供构造器表达式
2)通过反射调用Class.newInstance方法来构造泛型对象 - 不能构造泛型数组(?)
- 不能在静态域或方法中引用类型变量
public class Singleton<T>{
private static T singleInstance; //Error
public static T getSingleInstance(){ //Error
....
}
}
与泛型特性有关,可参见https://blog.csdn.net/aabbwoshishei/article/details/50187679的示例
- 不能抛出或补货泛型类的示例(?)
4.泛型类型的继承规则
泛型学习(一)中提到过泛型的继承规则,这种继承规则限制如此严格的原因与上文提到的不能创建数组的原因类似。
Pair<Manager> managers = new Pair<>(ceo,cfo);
Pair<Employee> employees = managers ; //illegal
employee.setFirst(lowlyEmploee);
managers和employees 实际上是引用的同样的对象,但由于泛型的类型擦除,使得managers 也能存放employees 对象,这对于managers 来说是不可能成立的。