第五章、泛型
泛型的作用:告诉编译器每个集合中可接受哪些对象类型,编译器自动地为你的插入进行转化,并在编译时告知是否插入错误的对象。这样使程序既更加安全也更加清楚。
第二十三条、请不要在新代码中使用原生态类型
-
声明中具有一个或者多个类型参数的类或者接口,就是泛型类或者接口.
例:List接口只有单个类型参数E,表示列表的元素类型。
List<E>
每种泛型定义一组参数化的类型,构成的格式为:先是类或者接口名,接着用尖括号<>把对应于泛型形式类型参数的实际类型参数列表括起来。如:List<String>
。 -
每个泛型都定义一个原生态类型(raw type),即不带任何实际类型参数的泛型名称。如:与List<E>对应的原生类型是List。但如果使用原生态类型,就失掉了泛型在安全性和表述性方面的所有优势。
-
安全的替代方法:无限制的通配符类型(unbounded wildcard type):
如果使用泛型,但不确定或者不关心实际的类型参数,就可以使用一个问号代替。
那么,Set<?>和Set的区别在哪?通配符类型是安全的,而原生态类型不安全。由于可以将任何元素放进原生态类型的集合中,因此很容易破坏该集合的类型约束条件。 -
两个例外情况:
在类文字中必须使用原生态类型,如:List.class,String[].class;
在instanceof中。 -
总结:使用原生态类型会在运行时导致异常,因此不要在新代码中使用,原生态类型只是为了引入泛型之前的遗留代码进行兼容和互用而提供的。Set<Object>是个参数化类型,表示可以包含任何对象类型的一个集合,Set<?>则是一个通配符类型,表示只能包含某种未知对象类型的一个集合,Set是个原生态类型。前两者是安全的,后面一种是不安全的。
第二十四条、消除非受检警告
-
泛型编程时会遇到许多编译器的警告:非受检强制转化警告(unchecked cast warning)、非受检方法调用警告、非受检普通数组创建警告、以及非受检转换警告(unchecked conversion warnings)。有些警告可以根据编译器来消除,但是有些警告难以消除,同时可以证明引起警告的代码是类型安全的,可以用一个
@SuppressWarnings("unchecked")
来禁止这条警告。 -
@SuppressWarnings("unchecked")
可以在任何粒度的级别中,应该始终在尽可能小的范围内使用SuppressWarnings注解。它通常是个变量声明,或是非常简单的方法或者构造器。
第二十五条、列表优先于数组
-
数组与泛型相比的不同点:
-
数组是协变的(covariant),即如果Sub为Super的子类型,即Sub[]为Super[]的子类型。而泛型是不可变的。这么看来,数组是有缺陷的。
//编译时是合法的 Object[] objectArray = new Long[1]; objectArray[0] = "I don't fit in"; //Won't Compile List<Object> ol = new ArrayList<Long>(); ol.add("I don't fit in");
-
数组是具体化的(reified),因此数组会在运行时才知道检查它们的元素类型约束;相比之下,泛型则是通过擦除(erasure)来实现的,因此泛型只在编译时强化它们的类型信息,并在运行时丢弃它们的元素信息。
-
由于上面的两个区别,数组和泛型不能很好地混合使用。
-
-
从技术的角度说:像
E
、List<E>
和List<String>
这样的类型应称作不可具体化的类型(直观上说是指其运行时表示法包含的信息比它编译时表示包含法的信息更少的类型。)唯一可具体化的参数化类型是无限制的通配符类型,如List<?
>和Map<?,?>
虽然不常用,但创建无限制通配符类型的数组是合法的。 -
总结:数组和泛型有着非常不同的类型规则。数组是协变且可以具体化。泛型是不可变的且可以被擦除,一般数组和泛型不能很好地混合使用,如果编译错误得到警告,第一反应时用列表代替数组。
第二十六条、优先考虑泛型
第二十七条、优先考虑泛型方法
第二十八条、利用有限制通配符来提升API的灵活性
-
Java提供了一种特殊的参数化类型,称作有限制的通配符类型:
<? extends E >
E的某个子类型,确定了子类型之后,每个类型都是自己的子类型。
<? super E>
E的某个超类,每个类型都是自己的超类。 -
为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符类型,如果某个输入参数既是生产者又是消费者,那么通配符类型就没有用了,因为你需要的是严格的类型匹配。
-
助记符:PECS: producer-extends,consumer-super:
如果参数化类型表示一个T生产者,就使用
<? extends T>
,如果表示一个T消费者,则使用<?super T>
,所有的comparable和comparator都是消费者。 -
不要用通配符类型作为返回类型! 通配符类型对于类的用户来说应是无形的。
第二十九条、优先考虑类型安全的异构容器
-
泛型最常用于集合(Set或者Map)以及单元素的容器。在这些用法中,它都充当被参数化了的容器。这样就限制了每个容器只能有固定数目的类型参数,一个Set只有一个类型参数,表示它的元素类型,一个Map有两个类型参数,表示它的键和值得类型。
-
可以通过将类型参数放在键上而不是容器上来避开这种限制,对于这种类型安全的异构容器,可以用Class对象作为键。