《Effective Java》第5章 泛型
第23条:请不要在新代码中使用原生态类型
声明中具有一个或者多个类型参数( type parameter)的类或者接口,就是泛型(generic)类或者接口。
每种泛型定义一组参数化的类型(parameterized type),构成格式为: 先是类或者接口的名称,接着用尖括号(<>)把对应于泛型形式类型参数的实际类型参数列表括起来。例如,List(读作“字符串列表”)是一个参数化的类型,表示元素类型为String的列表。
最后点,每个泛型都定义一个原生态类型[raw type],即不带任何实际类型参数的泛型名称。例如,List相对应的原生态类型是List。原生态类型就像从类型声明中删除了所有泛型信息一样。
如果使用原生态类型,就失掉了泛型在安全性和表述性方面的所有优势。
从Java 1.5发行版本开始,Java就提供了一种安全的替代方法,称作无限制的通配符类型(unbounded wildcard type). 如果要使用泛型,但不确定或者不关心实际的类型参数,就可以使用一个问号代替。例如,泛型Set的无限制通配符类型为Set<>(读作“某个类型的集合”)。
通配符类型是安全的,原生态类型则不安全。
不能将任何元素(除了null之外)放到Colllection<>中。
不要在新代码中使用原生态类型,这条规则有两个小小的例外,两者都源于“泛型信息可以在运行时被擦除”(见第25条)这一事实。在类文字(class literal)中必须使用原生态类型。规范不允许使用参数化类型(虽然允许数组类型和基本类型)。换句话说,List.class, String[].class和int.class都合法,但是List.class和List<>.class则不合法。
这条规则的第二个例外与instanceof操作符有关。由于泛型信息可以在运行时被擦除,因此在参数化类型而非无限制通配符类型上使用instanceo躁作符是非法的。用无限制通配符类型代替原生态类型,对instanceof操作符的行为不会产生任何影响。在这种情况下,尖括号(<>)和问号(?)就显得多余了。下面是利用泛型来使用instanceof操作符的首选方法:
第24条: 消除非受检警告
SuppressWarnings注解可以用在任何粒度的级别中,从单独的局部变量声明到整个类都可以。应该始终在尽可能小的范围中使用SuppressWarnings注解。它通常是个变量声明、或是作常简短的方法或者构造器。永远不要在整个类上使用SuppressWarnings,这么做可能会掩盖了重要的警告。
如果你发现自己在长度不止行的方法或者构造器中使用了SuppressWarnings注解,可以将它移到一个局部变最的声明中。
将SuppressWarnings注解放在return语句中是非法的,因为它不是一个声明。
每当使用SuppressWarnings("unchecked")注解时,都要添加一条注释,说明为什么这么做是安全的。
第25条:列表优先于数组
数组协变性的缺陷
数组与泛型相比。有两个重要的不同点。首先,数组是协变的(covariant)。即表示如果Sub为Super的子类型,那么数组类型Sub[]就是Super[]的子类型。相反,泛型则是不可变的(invariant): 对于任意两个不同的类型Type1和Type2 , List既不是List的子类型,也不是List的超类型。你可能认为,这意味着泛型是有缺陷的,但实际上可以说数组才是有缺陷的。
数组是具体的,泛型是擦除的
数组与泛型之间的第二大区别在于,数组是具体化的(reified )。因此数组会在运行时才知道并检查它们的元素类型约束。相比之下,泛型则是通过擦除(erasure)来实现的。因此泛型只在编译时强化它们的类型信息,并在运行时丢弃(或者擦除)它们的元素类型信息。
由于上述这些根本的区别,因此数组和泛型不能很好地混合使用。例如,创建泛型、参数化类型或者类型参数的数组是非法的。
非法定义的示例:
new List[]、new List[]和new E[]。这些在编译时都会导致一个generic array creation。
Note:
为什么创建泛型数组是非法的?因为它不是类型安全的。要是它合法,编译器在其他正确的程序中发生的转换就会在运行时失败,并出现一个CIassCastException异常。这就违背了泛型系统提供的基本保证。
一个反例:
假设以下第一行是可以编译通过的,那么会在第五行得到CIassCastException。
创建无限制通配类型的数组是合法
从技术的角度来说,像E, List和List这样的类型应称作不可具体化的(non-reifiaBle)类型。直观地说,不可具体化的(non-reifiable)类型是指其运行时表示法包含的信息比它的编译时表示法包含的信息更少的类型。唯一可具体化的(reifiable )参数化类型是无限制的通配符类型,如List<>和Map<>。虽然不常用,但是创建无限制通配类型的数组是合法的。
列表:用性能换取类型安全性
当你得到泛型数组创建错误时,最好的解决办法通常是优先使用集合类型List,而不是数组类型E[]。这样可能会损失一些性能或者简洁性。但是换回的却是更高的类型安全性和互用性。
总而言之,数组和泛型有着非常不同的类型规则。数组是协变且可以具体化的,泛型是不可变的且可以被擦除的。因此,数组提供了运行时的类型安全,但是没有编译时的类型安全,反之,对于泛型也样。一般来说,数组和泛型不能很好地棍合使用。如果你发现自己将它们混合起来使用,并且得到了编译时错误或者警告,你的第一反应就应该是用列表代替数组。
第27条:优先考虑泛型方法
类型导出
泛型方法的一个显著特性是,无需明确指定类型参数的值,不像调用泛型构造器的时候是必须指定的。编译器通过检查方法参数的类型来计算类型参数的值。这个过程称为类型导出(type inference)。
相关的模式是泛型单例工厂(generic singleton factory )。有时,会需要创建不可变但又适合于许多不同类型的对象。
第28条:利用有限制通配符来提升API的灵活性
例子:
假设我们想要在Stack中增加一个方法,让它按顺序将一系列的元素全部放到堆栈中。
考虑以下反例:
但是,如果尝试这么做,就会在以下代码得到错误消息。因为如前所述,参数化类型是不可变的。
幸运的是,有一种解决办法。Java提供了一种特殊的参数化类型,称作有限制的通配符类型(bounded wildcard type),来处理类似的情况。pushAll的输入参数类型不应该为“E的Iterable接口”,而应该为“E的某个子类型的Iterable接口”,有一个通配符类型正符合此意:
Iterable.
修改后的代码如下:
"E的某种超类的集合”(这里的超类是确定的,因此E是它自身的一个超类), 仍然有一个通配符类型正是符合此意:Collection<? super E)。
PECS
结论很明显口为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符类型。如果某个输入参数既是生产者。又是消费者,那么通配符类型对你就没有什么好处了:因为你需要的是严格的类型匹配,这是不用任何通配符而得到的。
下面的助记符便于让你记住要使用哪种通配符类型:
PEGS表示producer-extends, consumer-super.
换句话说,如果参数化类型表示一个T生产者,就使用,如果它表示一个T消费者,就使用。
Note:
不要用通配符类型作为返回类型。
总而言之,在API中使用通配符类型虽然比较需要技巧,但是使API变得灵活得多。如果编写的是将被广泛使用的类库,则一定要适当地利用通配符类型。记住基本的原则:producer-extends, consumer-super (PECS)。还要记住所有的comparable和comparator都是消费者。
第29条:优先考虑类型安全的异构容器
异构容器是指能够容纳不同类型对象的容器。像我们通常用的List、Map等容器,它们的原生态类型本身就是异构容器,一旦给它们设置了泛型参数,例如List、Map,它们就不再是异构容器。但是,原生态类型是不安全的,你无法知道从容器取出的类型到底是什么,很容易导致错误。因此,如何构建类型安全的异构容器就成了一个重要的话题。
使用Map实现类型安全的异构容器
我们将要实现一个Favorites类,用来对每个类型保存一个最喜欢的实例。它的API如下:
public class Favorites {
public <T> void putFavorite(Class<T> type, T instance);
public <T> T getFavorite(Class<T> type);
}
Favorite,的实现小得出奇。它的完整实现如下
但是,恶意的客户端可以破坏Favorites实例的类型安全。如果客户端传入原生态的Class对象和不一致的值对象,则会在getFavorite的cast时抛出ClassCastException异常。不过好在我们可以对这一情况加以约束。只需要在put时使用一个动态的转换就可以了:
将put方法更新为以下方法:
参考资料
《Effective Java》第5章 泛型的更多相关文章
- [Effective Java]第五章 泛型
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- EFFECTIVE JAVA 第十一章 系列化
EFFECTIVE JAVA 第十一章 系列化(将一个对象编码成一个字节流) 74.谨慎地实现Serializable接口 *实现Serializable接口付出的代价就是大大降低了“改变这个类 ...
- effective java 第2章-创建和销毁对象 读书笔记
背景 去年就把这本javaer必读书--effective java中文版第二版 读完了,第一遍感觉比较肤浅,今年打算开始第二遍,顺便做一下笔记,后续会持续更新. 1.考虑用静态工厂方法替代构造器 优 ...
- [Effective Java]第六章 枚举和注解
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- [Effective Java]第七章 方法
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- [Effective Java]第四章 类和接口
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- [Effective Java]第三章 对所有对象都通用的方法
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- 对于所有对象都通用方法的解读(Effective Java 第三章)
这篇博文主要介绍覆盖Object中的方法要注意的事项以及Comparable.compareTo()方法. 一.谨慎覆盖equals()方法 其实平时很少要用到覆盖equals方法的情况,没有什么特殊 ...
- Effective.Java第23-33条(泛型相关)
23. 类结构层次优于标签类 有时你会碰到一个类,它的实例有一个或多个风格,并且包含一个tag属性表示实例的风格.例如,如下面的类表示一个圆或者矩形: public class Figure { / ...
- Effective Java 读书笔记之四 泛型
泛型的本质是参数化类型.只对编译器有效. 一.请不要在新代码中使用原生态类型 1.泛型类和接口统称为泛型,有一个对应的原生态类型. 2.原生类型的存在是为了移植兼容性. 3.无限制通配类型和原生态类型 ...
随机推荐
- NOIP模拟题 栅栏
题目大意 给定一个$n\times m$的网格图,每次会选择一块矩形沿着网格线铺上栅栏,或者拆除之前铺的栅栏,或者询问两个格子能否不经过栅栏直接到达. 保证栅栏没有重叠或交叉,删去的栅栏删除前一定存在 ...
- UVA - 11014 Make a Crystal (莫比乌斯反演)
给定一个n*n*n的立方体(中心点为原点O),选择尽量多的点,使得对于任意两点A,B,B不在线段OA上. 可以发现,原问题可转化为三维坐标下的点(x,y,z)中有多少个点的gcd(x,y,z)=1. ...
- Js中的prototype的用法一
一 prototype介绍 prototype对象是实现面向对象的一个重要机制.每个函数也是一个对象,它们对应的类就是function,每个函数对象都具有一个子对象prototype.Prototyp ...
- sleep(0)作用
假设现在是 2008-4-7 12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在 2008-4-7 12:00:01.000 的时候,这个线程会 不会被唤醒?某人的代 ...
- C++空类大小
class a {};class b{};class c:public a{ virtual void fun()=0;};class d:public b,public c{}; 类a,b明明是空类 ...
- Mstar 编译器的搭建
机顶盒: 1.解压“mipsisa32-elf-3.4.4-20101123.i386linux.tar.gz" 应用编译器 2.mips-4.3-51-mips-linux-gnu-i68 ...
- Angular5学习笔记 - 服务优化(十)
一.服务合并 二.验证效果
- QString 乱谈(1)
一个月前尝试写了一篇关于QStringLiteral,存盘时MoinMoin罢工了.吸取一点经验,还是写成短篇吧 可是,可是,QString不就是简简单单一个字符串么?能有什么可谈的.真的么... ( ...
- java代码I/O类
总结:流类无法理解啊—————— package com.aini; import java.io.*; //流类 //使用FileInputStream读取文件信息 public class ffg ...
- RCE、exp、Exploit、Exploit Pack、exp-gui、Payload、MetaSploit都是啥
对于走在安全路上的小菜来说,这几个exp.Exploit.Exploit Pack.exp-gui.Payload.MetaSploit名词着实把人转的不轻,下面给大家解释下: RCE,remote ...