Java泛型学习笔记

来源:互联网 发布:关口知宏妻子 编辑:程序博客网 时间:2024/05/13 21:35

这是一篇零散的学习笔记,涉及到一些Java泛型遇到的知识点,不是系统学习

语法糖

 泛型算是一种语法糖,本质上和直接存Object是一样的,只不过通过泛型,编译器会对这个类做一个类型转换,能将问题发现在编译期。

区分:泛型类和类型参数

class A<T>{    T t;}

 在上面的实例中,A是泛型类,而T是泛型参数。

擦除

 前面说到,泛型是一种语法糖,所以在虚拟机中,是没有泛型这个概念的,所以在编译时,编译器会把这些泛型参数转为其限定类型(如果没有限定上界,那就是Object),就叫做擦除。
 因为擦除,运行时的类型信息就没有了,所以如果不做强制类型转换(显式地或者隐式的),那些需要在运行时知道确切信息的操作都无法进行(方法调用、创建类)。擦除很关键,几乎泛型所有的限制都与擦除这个机制有关。

通配符

 前面说了通配符的概念,大概就是<? extends BoundingType>或者<? super BoundingType>这个BoudingType就叫做上界或者下界?就叫做通配符。表示某个类的子类或者某个类的父类。这样的限定类型是可以用在定义和申明时的。下面一段代码说明了类的定义、申明和创建三个阶段。

public class A<? extends B>{ public static void main(String[] args){ List<? extends B> lists = new ArrayList<>(); }} 

 这里的class A...就是类的定义部分,而List<? extends B> lists是对List这个类进行申明,而new ArrayList<>()是对类的创建部分。基本上,通配符在定义时的作用是限制子类或者在定义是确定参数类型(并给其确定一个限定上界/下界),通配符在申明时的作用是限定在创建确定类型参数(注意这个参数的限定上界仍然是在定义时确定的)。

菱形语法:Java7特性

 如果在创建某个类时,申明类时确定了类型参数,那么创建中就不必再申明具体的类型参数,如之前的代码中,

List<? extends B> lists = new ArrayList<>();

 这里ArrayList<>()中是<>而不是<B>之类的,是因为在申明时就从语法上(没错加粗是因为我还是要强调其实本质上在运行时还是Object或者是限定类型),能确定这个类型参数了。注意这个要在Java7及以上用。

泛型类的数组初始化不合法

 根本原因是,数组在创建的时候必须知道内部元素的类型,而且一直都会记得这个类型信息。而泛型类在创建时是不能确定类型的,考虑下面的例子:

List<String>[];List<Integer>[];

 看起来截然不同的两个类,其实在创建时都是List[](List<Object>[]),所以因为参数类型不能确定,所以数组不支持初始化。但是可以通过强制类型转换的方式的生成数组,例如如下的代码

        //错误        // List<String>[] list1 = new List<String>[2];        //正确        List<String>[] list2 = (List<String>[]) new List[2];

参数化类型不能做需要运行时信息的事情

 这个之前有所提到,即擦除的限制。比如下面的操作是非法的

public class A<T>{    public static void main(String[] args){        //直接通过new初始化不行        //T t = new T();        //调用不行,除非做了强制类型转换        //t.say();    }}

参数化类型作为静态上下文

 静态上下文指的是,静态成员变量、静态代码块、静态方法。下面一段代码也是非法的。因为多个类都会共用这些静态的上下文,不同的实例申明创建时对待T可能是不同类型,这意味这就需要在编译时插入不同的类型转换代码,从而导致错误。

public class A<T> {    //非法    private static T t;    //非法    static{    T t;    }    //非法    public static void f(){    T t;    }}

泛型与异常

 泛型类是不能继承Throwable及其子类的,具体原因看下面这个例子:

//这段代码是错误的try {   doSomeStuff();} catch (SomeException<Integer> e) {   // ignore that} catch (SomeException<String> e) {}

 其实本质上还是类型擦除的原因,这里明显两个异常类是一样的(运行时都是Object),显示是不行的,所以泛型类是不能继承Throwable及其子类的,而且泛型类也不能被捕获或者抛出。
 虽然泛型类不能被抛出,但是这不妨碍类型参数被抛出,只要我们在最初设置一个类型参数的上界即可。

0 0