泛型简介
Java泛型是jdk1.5引入的新特性,但并不是由Java所引入的新概念,在Java正式使用泛型之前如果要实现类型泛化,都是通过直接在参数类型处使用Object,因为Object是所有类的父类,所以编译期都不会报错,可是一旦到了运行期,你不太可能确定真实的类型能够正确转换成你想要的类型。
在没有泛型之前:
/**
* Son extends Father
*
* @author myllxy
* @create 2019-12-15 22:04
*/
public class GenericTest1 {
public static void main(String[] args) {
testDemo1(new Father());
}
/**
* 该方法中参数object是静态类型,静态类型的变化仅仅在使用时发生,
* 变量本身的静态类型不会改变,并且最终的静态类型是在编译期可知的;
* 而实际类型变化的结果在运行期在可以知道,编译器在编译程序的时候病
* 不知道一个对象的实际类型是什么
*
* 所以,在这个案例中我们可以看到,虽然最后报类型转换错误,但是编译期
* 不会报错,因为Father确实能够直接向上转型成Object,注意着后面,
* (Son)object是在=右边,相当于编译期编译期并不知道object是什么了
* 他只是为了通过编码规定将object加了个(Son),但至于是否真的能转换
* 看运行期了
*/
private static void testDemo1(Object object) {
Son son = (Son) object;
}
}
所以到这儿我们就看到没有泛型时程序的弊端了,在加上泛型之后:
/**
* Son extends Father
*
* @author myllxy
* @create 2019-12-15 22:04
*/
public class GenericTest1 {
public static void main(String[] args) {
testDemo1(new Father());
}
/**
* 在加上泛型后上面的testDemo1(new Father())就会报错了
* 具体T extends Son牵涉到的泛型的协变与逆变问题不在这里赘述了
*/
private static <T extends Son> void testDemo1(T t) {
Son son = (Son) t;
}
}
这就是泛型,它为我们在编译期就讲运行期可能的类型转换错误提前预知出来了
泛型擦除
先看这么一个案例:
按照重载的定义,List<Integer>、List<String>理论上是两个不同的类型吧,但是仍然在编译期被拒绝编译了
erasure这个单词就是擦除的意思
泛型中的类型在编译时被擦除,运行时会被替换为原生类型,所以List<Integer>、List<String>在运行期就是同一个类型,编译器就理所当然地阻止编译了。
这段代码的字节码如下:
Signature用于存储一个方法在字节码层面的特征签名
对泛型类型的局部变量,需要在LocalVariableTable Attribute和LocalVariableTypeTable Attribute中同时存在一项;而对非泛型类型的局部变量来说,只要在LocalVariableTable Attribute存在表项就可以了。
但是,仅仅对code属性中的字节码进行擦除,实际上元数据还是保留了泛型信息,这也是我们能通过反射来获取其参数化类型事务根本依据