Effective Java读书笔记七:泛型(23-29 部分章节需要重读)

第23条:请不要在新代码中使用原生态类型

从java1.5发行版本开始,java就提供了一种安全的替代方法,称作无限制的通配符类型,如果要使用范型,但是确定或者不关心实际的参数类型,就可以用一个问号代替。例如范型Set<?>的无限制通配符类型为Set<?>。这是最普通的参数化Set类型,可以持有任何集合。

关于Set<?>的两个事实:
1、因为“?”标记可以代表任何类型,Set<?>可以持有任何类型的元素
2、由于不知道“?”代表的类型,所有我们不能把任何元素放到Set<?>集合中

由于可以将任何元素放进使用原生态类型的集合中,因此很容易破坏该集合的类型约束条件;但不能将任何元素(除了null之外)放到Collection<?>中。

“不要在新代码中使用原生态类型”,这条规则有两个例外,这是因为“泛型信息在运行时就会被擦除”。

在获取类信息中必须使用原生态类型(数组类型和基本类型也算原生态类型),规范不允许使用参数化类型。换句话说:List.class,String[].class和int.class都是合法,但是List<String>.class和List<?>.class都是不合法的。

这条规则的第二个例外与instanceof操作符有关,由于泛型信息在运行时已被擦除,因此在参数化类型而不是无限制通配符类型(如List<?>)上使用instanceof操作符是非法的,用无限制通配符类型代替原生态类型,对instanceof操作的行为不产生任何影响。在这种情况下,尖括号<>和问号?就显得多余了。

下面是利用泛型来使用instanceof操作符的首先方法:

if(o instanceof set){
       Set<?> m = (Set<?>)o;
       // ...
}

注意,一旦确定这个o是个Set,就必须将它转换成通配类型Set<?>,则不是转换成原生态类型Set,否则Set会引起编译时警告。

总之,使用原生态类型会在运行时导致异常,因此不要在新代码中使用。原生态类型只为了与引入泛型之前的遗留代码进行兼容和互用而提供的。另外Set<Object>是个参数化类型,表示可以包括任何对象类型的一个集合;Set<?>则是一个通配符类型,表示只能包含某种未知对象类型的一个集合;Set则是个原生态类型,它脱离了泛型系统。前两者是安全的,最后一种不安全。

术语介绍:
原生态类型:List
参数化的类型:List<String>
泛型:List<E>
有限制类型参数:List<E extends Number>
形式类型参数:E
无限制通配符类型:List<?>
有限制通配符类型:List<? extends Number>
递归类型限制:List <T extends Comparable>
泛型方法: static<E> List<E> asList(E[] a)

第24条:消除非受检警告

用泛型编程时,会遇到许多编译器警告:非受检强制转换警告、非受检方法调用警告、非受检普通数组创建警告,以及非受检转换警告。

要尽可能地消除每一个非受检警告。如果消除了所有警告,就可以确保代码是类型安全的。

如果无法消除警告,同时可以证明引起警告的代码是类型安全的,只有在这种情况下才可以用一个@SuppressWarnings(“unchecked”)注解来禁止这条警告。

SuppressWarnings注解可以用在任何粒度的级别中,从单独的局部变量到整个类都可以。应该始终在尽可能小的范围中使用SuppressWarnings注解。它通常是个变量声明,或者是非常简短的方法或者构造器。永远不要在整个类上使用SuppressWarnings,这么做可能会掩盖了重要的警告。

总而言之,非受检警告很重要,不要忽略它们。每一条警告都表示可能在运行时抛出ClassCastException异常。要尽最大的努力消除这些警告。如果无法消掉同时确实是类型安全的,就可以在尽可能小的范围中,用@SuppressWarnings(“unchecked”)注解来禁止这条警告。要用注释把禁止该警告的原因记录下来。

第25条:列表优先于数组

1、数组是协变的(convariant),如果Sub是Super的子类型,那么数组类型Sub[]就是Super[]的子类型。
泛型确实不可变的,List<Sub>不是List<Super>的子类型。

2、数组是具体化的(reified),因此数组在运行时才知道并检查它们的元素类型约束。
泛型则是通过擦除(erasure)来实现,因此泛型只在编译时强化它们的类型信息,并在运行时丢弃(或者擦除)它们的元素类型约束。擦除就是使泛型可以与没有使用泛型的代码随意进行互用(见第23条)。
下面代码片段是合法的:

Object[] objectArray = new Long[1];
objectArray[0] ="I don't fit in";//Throws ArrayStoreExecption

但是下面的代码则是不合法的:

List<Object> ol = new ArrayList<Long>();//Incompatible types 不兼容类型
ol.add("I don't fit in");

这其中无论是那种方法,都不能将String类型放进Long容器中,但是利用数组,你会在运行时抛出所犯的错误,利用List列表,则是在编译时就能看到发生的错误,我们当然希望在编译时发现错误了。

由于上述这些根本的区别,因此数组和泛型不能很好的混合使用。例如,创建泛型,参数化类型或者类型参数的数组是非法的。这些数组创建表达式没有一个是合法的:new List<E>、new List<String>[]和new E[]。这些在编译的时都会导致一个generic array creation(泛型创建数组)错误。

第26条:优先考虑泛型(需要重新读)

使用泛型比使用需要在客户端代码中进行转换的类型来的更加安全,也更加容易。在设计新类型的时候,要确保它们不需要这种转换就可以使用。这通常意味着要把类做成是泛型的。

第27条:优先考虑泛型方法(需要重新读)

泛型方法就想泛型对象一样,使用起来比要求客户端转换输入参数并返回值的方法来得更加安全,也更加容易。就像类型一样,你应该确保新方法可以不用转换就能使用,这通常意味着要将它们泛型化。

第28条:利用有限制通配符来提升API的灵活性(需要重新读)

为了获得最大限度的灵活性,要在表示生产者和消费者的输入参数上使用通配符类型。如果某个输入参数即是生产者,又是消费都,那么通配符类型对你就没有什么好处了,因为你需要严格的类型匹配,这是不用任何通配符而得到的。

下面的助记符便于让你记住要使用哪种通配符类型:

PECS 表示:producer-extends, consumer-super
换句话说,如果参数化类型表示一个T生产者,就使用<? extends T>;如果它表示一个T消费者,就使用<? super T>。

PECS这个助记符突出了使用通配符的基本原则。Naftalin和Wadler称之为Get and Put Principle

第29条:优先考虑类型安全的异构容器(需要重新读)

集合API说明了泛型的一般用法,限制你每个容器只能有固定数目的类型参数。你可以通过将类型参数放在键上而不是容器上这一限制。对于这种类型安全的异构容器,可以用Class对象作为键。以这种方式使用的Class对象称作类型令牌。

《Effective Java中文版 第2版》PDF版下载:
http://download.csdn.net/detail/xunzaosiyecao/9745699

作者:jiankunking 出处:http://blog.csdn.net/jiankunking

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值