一、何为泛型?
jdk1.5之后引入泛型概念,可定义泛型类、接口、方法,且编译期会将泛型擦除,向下兼容;
二、泛型的优点?
2.1 编辑代码时保证类型安全,且省去强制类型转化
1.5之前代码:
List list = new ArrayList();
list.add(1);
list.add("test");
String element = (String)list.get(0);//编译器会报错
element = (String) list.get(1); //强制类型转化
1.5引入泛型之后:
List<String> list = new ArrayList<>();
list.add(1);//编译器报错
list.add("test");
String element = list.get(0);//不用强制类型转化
2.2 代码复用
比如实现数字排序的算法sort();
定义 public static <T> void sort(T[] a, Comparator<? super T> c),所有实现了Comparator的接口的类型都可以进 行排序算法,如果没有泛型,则需要针对int,double,float等各自实现一套sort算法
三、泛型的使用
3.1 泛型类、接口一样
public class A<T> {};
punlic classA<T extends B> {}
3.2 泛型方法(返回类型之前必须有<>),可以定在普通类也可定义在泛型类,但是在泛型类中定义时各自维护各自的泛型T
public <T> void set(T t);
public <T extends B> void set(T t);
public <T> T get();
四、通配符 ? 灵活转型(如网络架构Rxjava)
4.1 非限定通配符 ?; A<?> a 等价于 A<? extends Object> 既不能读也不能写,但作为方法入参可进行类型安全检验,另外作为引用时只进行Object相关操作也可,最典型的是Class<?> clz;
4.2 上界通配符<? extends T>; 只能读不能存(原因下面的继承关系中讲)
4.3 下界通配符<? super T>; 只能存且存T或者T的子类,不能读T,但是能读返回Object
五、泛型与继承、多态
5.1 继承:
class A<T>{}; class B<T> extends A<T> 或者 class B extends A<String>
class C extends D{}, class D extends E{}; List<C> 与 List<D>没有继承关系; 但是List<C>是ArrayList<C>的父类,只要泛型不变,则类的继承关系不变;
List<?> -> List<? extends D> -> (List<D> 和List<C>) 父类关系成立(左侧是父类) ;
List<? extends D> list = new ArrayList<D>(); 或者 List<? extends D> list = new ArrayList<C>();正因为这层继承关系,所以list引用可执行D的容器,也可指向C的容器,对于JVM来说,不清楚你最后可能指向的是哪个容器,所以禁止 上界通配符定义的变量能存放数据,否则会涉及类型安全问题。
List<?> -> List<? super D> -> (List<D>和List<E>)
六、JVM如何实现泛型?
6.1 编译前进行类型检查,编译后进行类型擦除,擦除的泛型信息保存在常量池中,通过反射可获取;
List<C> listC = new ArrayList<>(); List<D> listD = new ArrayList<>(); listC.getClass 与 listD.getClass是一样的,都是List;这就是类型擦除;
6.2 类型擦除原则如下:
没有限定则Object
有则用限定类型
桥方法维护多态性
擦除了但依旧可以通过反射获得泛型类,类常量池有保存泛型类信息
6.3 泛型后遗症如下:
1、不能实例化类型变量 new T(),可以通过反射获得实例;
2、static T t static void set(T t)静态属性或者方法里不能引用类型变量,对象创建的时候才知道类型,static属于类,先执行,此时还不知道具体是什么类型;静态泛型方法可以static <T> void set(T t)
3、基本类型不支持作为实参,List<double> list 不行,List<Double> 可以
4、instanceof不支持(泛型擦除导致,没有了类型信息) 如果是A<? extents B> 这个能用instanceof吗?
5、泛型数组不支持
A extents B A[] 的分类是 B[] 数组的协变
类型擦除后不能支持协变了
6、泛型类不能extends Exception/Throwable,也不能catch泛型类对象,但是可以catch exception然后throws出来即可
七、总结:
泛型设计初衷就是为了灵活的实现类型转型,这样设计和开发通用功能模块时可对用户透明,解耦且通用,强行记住上下界规则PECS(Product extends Consumer super),上不存下不取或者上只读下只写。