java泛型
最近在看hollis的书,对java的泛型好像有了更加深刻的认识。所以特意记录一下。
泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化的时候作为参数指明这些类型。
泛型分类和表示
- 泛型接口
public interface className<类型名称>{} 如 public interface Iterable<T>
- 泛型类
public class 类名称<类型参数>{} 如 public class ArrayList<E> {}
- 泛型方法
public <类型参数> 返回值类型 方法名(参数类型,参数列表) 如 public <T> T[] toArray(T[] a){}
泛型擦除
在java语言中,不同的泛型类,经过jvm编译以后,会当成同一个类进行处理。所以,泛型技术实际上是Java语言的语法糖,因为泛型经过编译器处理之后就被擦除了,编译器根本不认识泛型。这个擦除的过程叫做类型擦除,指的是,通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并且将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且在必要时添加类型检查和类型转换的方法。
泛型擦除的边界
- 将所有的泛型参数用其最左边界(顶级的父类型)类型进行替换。
- 移除所有的类型参数。
泛型与桥接方法
那么简单粗暴的把泛型的信息去掉,会不会有影响呢。
public interface Parent <T>{
public void set(T t);
}
public class Child implements Parent<String>{
@Override
public void set(String s) {
System.out.println("test");
}
}
因为存在泛型擦除,那么父类的set(T t)
就会变为 set(Object o)
; 而子类中的set(String s)
和父类就不满足重写关系了, 而是 重载(传入参数不同)。对于这个,JVM的解决方案是 在 子类中生成一个 set(Object o)
方法,然后在该方法中调用 set(String s)
泛型带来的问题
- 泛型不支持基本数据类型
- 泛型会影响重载 即
public void test(List<String>list){}
和public void test(List<Integer>list)
因为编译后都是public void test(List list)
所以这样的重载会导致编译出错; - instanceof不能直接用于泛型比较 如
if (obj instanceof T)...
List<Integer>
不是List<Object>
的子类,因为经过泛型擦除后,这两个都会变成 List list- 泛型中的静态变量只有一份
public class Holder<T>{
public static int var =0;
}
public class HolderTest{
public static void main(String[] args){
Holder<String> holder1 =new Holder<>();
holder1.var =1;
Holder<Integer>holder2 =new Holder<>();
holder2.var =2;
System.out.println(holder1.var);
}
}
这个编译后的结果为2;因为经过泛型擦除,所有的泛型类都指向同一份字节码上。泛型类的所有静态变量是共享的。