1 什么是擦除
从以上的代码可以看出,List<String>
和 List<Integer>
在运行时事实上是相同的类型,这两种形式都被擦除成它们原生的类型。
Java 泛型是使用擦除来实现的,当你在使用泛型的时候,任何具体的类型信息都会被擦除掉,你唯一知道的就是你在使用一个对象。
由以上的代码可知, Class.getTypeParameters() 将返回一个 TypeVariable 对象数组,表示有泛型声明所声明的类型参数。但是,返回的只是用作参数占位符的标识符,这并非有用的信息。
残酷的事实:在泛型代码的内部,无法获得任何有关泛型参数类型的信息。
你可以知道诸如:类型参数标识符、泛型类型边界这类信息,却无法知道用来创建某个特定实例的实际的类型参数。
2 类型擦除后保留的原始类型
原始类型(raw type)就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型。
无论何时定义一个泛型类型,相应的原始类型都会被自动地提供。类型变量被擦除(erasure),并使用其限定类型(无限定的变量用Object)替换。
无限定的类型参数
class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
Pair<T>
的原始类型为:
class Pair {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
因为在Pair<T>
中,T是一个无限定的类型变量,所以用Object替换,其结果就是一个普通的类。
在程序中可以包含不同类型的Pair,如Pair<String>
或Pair<Integer>
,但是,擦除类型后它们就成为原始的Pair类型了,原始类型都是Object。
有限定(边界)的类型参数
如果类型变量有限定,那么原始类型就用第一个边界的类型变量来替换。
public class Pair<T extends Comparable& Serializable> { }
原始类型为 Comparable。
public class Pair<T extends Serializable&Comparable > { }
原始类型为 Serializable。
而编译器在必要的时要向Comparable插入强制类型转换。为了提高效率,应该将标签(tagging)接口(即没有方法的接口)放在边界限定列表的末尾。
泛型类型参数将擦除到它的第一个边界(它可能会有多个边界)。
类型参数的擦除,编译器实际会把类型参数替换为它的擦除。
像上面的例子中,T 擦除到了 Serializable。
3 原始类型与泛型变量的类型
在调用泛型方法的时候,可以指定泛型,也可以不指定泛型。
在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一个父类的最小级,直到Object。
在指定泛型的时候,该方法中的几种类型必须是该泛型实例类型或者其子类。
public class Test2{
public static void main(String[] args) {
/**不指定泛型的时候*/
int i=Test2.add(1, 2); //这两个参数都是Integer,所以T为Integer类型
Number f=Test2.add(1, 1.2);//这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Number
Object o=Test2.add(1, "asd");//这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Object
/**指定泛型的时候*/
int a=Test2.<Integer>add(1, 2);//指定了Integer,所以只能为Integer类型或者其子类
int b=Test2.<Integer>add(1, 2.2);//编译错误,指定了Integer,不能为Float
Number c=Test2.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float
}
//这是一个简单的泛型方法
public static <T> T add(T x,T y){
return y;
}
}
其实在泛型类中,不指定泛型的时候,也差不多,只不过这个时候的泛型类型为Object,就比如ArrayList中,如果不指定泛型,那么这个ArrayList中可以放任意类型的对象。
public static void main(String[] args) {
ArrayList arrayList=new ArrayList();
arrayList.add(1);
arrayList.add("121");
arrayList.add(new Date());
}
4 类型擦除引起的问题及解决方法
4.1 先检查,在编译,以及检查编译的对象和引用传递的问题
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<String>();
arrayList.add("123");
arrayList.add(123);//编译错误
}
http://blog.csdn.net/lonelyroamer/article/details/7868820
抄不下去了…
编译,运行,检查傻傻的分不清楚。
等我学完了 JVM 再回来填坑