The Java™ Tutorials —— Generics 导读
2016-02-10 12:17
489 查看
The Java™ Tutorials —— Generics 导读
笔者在此将此前通篇翻译的Oracle官方文档泛型部分加以整理如下:The Java™ Tutorials —— Generics:前言
泛型可以增强编译时错误检测,减少因类型问题引发的运行时异常The Java™ Tutorials — Generics :Why Use Generics? 为什么使用泛型
泛型具有更强的类型检查泛型可以避免类型转换
泛型可以泛型算法,增加代码复用性
The Java™ Tutorials — Generics :Generic Types 泛型
如果利用Object来达到通用存储的目的,那么可能引起运行时错误泛型类格式:
class name<T1, T2, ..., Tn>
常见类型变量名:E(元素)、K(键)、N(数字)、T(类型)、V(值)、S(第二类型参数)、U(第三类型参数)
*“类型参数”与“类型变量”的不同
Foo<T>中的T为类型参数
Foo<String>中的String为类型变量
钻石运算符的使用
The Java™ Tutorials — Generics :Raw Types 原始类型
定义:缺少实际类型变量的泛型就是一个原始类型
举例:
class Box<T>{} Box b = new Box(); //这个Box就是Box<T>的原始类型
实际行为:获取和返回的类型都为Object
缺点:无法进行编译时类型检查,将异常的捕获推迟到了运行时,还可能会收到unchecked警告。
对unchecked警告的处理
@SuppressWarnings(“unchecked”)
-Xlint:unchecked
The Java™ Tutorials — Generics :Generic Methods 泛型方法
定义格式:private <K,V> boolean compare(Pair<K,V> p1, Pair<K,V> p2)
调用格式:
Util.compare(p1,p2)
The Java™ Tutorials — Generics :Bounded Type Parameters 受限的类型参数
功能:对泛型变量的范围作出限制格式:
单一限制:
<U extends Number>
多种限制:
<U extends A & B & C>
extends表达的意义:这里指的是广义上“扩展”,兼有“类继承”和“接口实现”之意
多种限制下的格式语法要求:如果上限类型是一个类,必须第一位标出,否则编译错误
题外问题:如果多种限制中的A和B同时为类该何如?
答:编译错误,这违反了Java不许多重继承的原则
The Java™ Tutorials — Generics :Generic Methods and Bounded Type Parameters 泛型方法和受限类型参数
泛型算法实现的关键:利用受限类型参数The Java™ Tutorials — Generics :Generics, Inheritance, and Subtypes 泛型,继承和子类型
泛型间父子关系The Java™ Tutorials — Generics :Type Inference 类型推断
理解编译器是如何利用目标类型来推算泛型变量的值注意下面的代码:在Java7中无法编译通过,而在Java8中却可以。Java8的编译器可以通过方法形参类型对泛型变量进行推断
static <T> List<T> emptyList(); void processStringList(List<String> stringList) { // process stringList } processStringList(Collections.emptyList());
The Java™ Tutorials — Generics :Wildcards 通配符
通配符的适用范围:参数类型
字段类型
局部变量类型
返回值类型(但返回一个具体类型的值更好)
The Java™ Tutorials — Generics :Upper Bounded Wildcards 受上限控制的通配符
语法格式:The Java™ Tutorials — Generics :Unbounded Wildcards 非受限通配符
两个关键使用场合:写一个方法,而这方法的实现可以利用Object类中提供的功能时
泛型类中的方法不依赖类型参数时
如List.size()方法,它并不关心List中元素的具体类型
List<XXX>是
List<?>的一个子类型
理解
List<Object>和
List<?>的不同:差在NULL处理,前者不支持,而后者却可接受一个null入表
The Java™ Tutorials — Generics :Lower Bounded Wildcards 有下限通配符
功能:限定了类型的下限,也就它必须为某类型的父类格式:
<? super A>
The Java™ Tutorials — Generics :Wildcards and Subtyping 泛型和子类
The Java™ Tutorials — Generics :Wildcard Capture and Helper Methods 通配符匹配和辅助方法
利用辅助方法解决通配符类型推断问题:原理:利用泛型的类型推断
public class WildcardFixed { void foo(List<?> i) { fooHelper(i); } // Helper method created so that the wildcard can be captured // through type inference. private <T> void fooHelper(List<T> l) { l.set(0, l.get(0)); } }
辅助方法格式:“所辅助的方法的名称+Helper”
The Java™ Tutorials — Generics :Guidelines for Wildcard Use 通配符使用指南
“输入”和“输出”变量的定义输入:负责添加数据到代码中,类比一个入口
输出:负责接收输入的数据,并将其传到需要的地方,类比一个出口
通配符准则:
一个“输入”变量是通过有上限通配符定义的,它使用了extends关键字
一个“输出”变量是通过有下限通配符定义的,它使用了super关键字
当“输入”变量可通过Object类中的方法获取时,那就使用一个无限制通配符
当代码需要获取一个兼具“输入”和“输出”功能的变量时,就不要使用通配符了
List
The Java™ Tutorials — Generics :Type Erasure 类型消除
功能:保证了泛型不在运行时出现类型消除应用的场合:
编译器会把泛型类型中所有的类型参数替换为它们的上(下)限,如果没有对类型参数做出限制,那么就替换为Object类型。因此,编译出的字节码仅仅包含了常规类,接口和方法。
在必要时插入类型转换以保持类型安全。
生成桥方法以在扩展泛型时保持多态性。
The Java™ Tutorials — Generics :Erasure of Generic Methods 泛型方法的类型擦除
消除方法:同对泛型类的处理无限制:替换为Object
有限制:替换为第一受限类型
The Java™ Tutorials — Generics :Effects of Type Erasure and Bridge Methods 类型擦除的影响以及桥方法
补充阅读:http://www.cnblogs.com/ggjucheng/p/3352519.html桥方法的功能:防止类型擦除后子类无法override父类的方法,保护泛型的多态性
The Java™ Tutorials — Generics :Non-Reifiable Types 不可具体化类型
可具体化类型和不可具体化类型的定义:可具体化类型:就是一个可在整个运行时可知其类型信息的类型。
包括:基本类型、非泛型类型、原始类型和调用的非受限通配符。
不可具体化类型:无法整个运行时可知其类型信息的类型,其类型信息已在编译时被擦除:
例如:
List<String>和
List<Number>,JVM无法在运行时分辨这两者
堆污染:
发生时机:当一个参数化类型变量引用了一个对象,而这个对象并非此变量的参数化类型时,堆污染就会发生。
分模块对代码进行分别编译,那就很难检测出潜在的堆污染,应该同时编译
带泛型的可变参数问题:
T…将会被翻译为T[],根据类型擦除,进一步会被处理为Object[],这样就可能造成潜在的堆污染
避免堆污染警告
@SafeVarargs:当你确定操作不会带来堆污染时,使用此注释关闭警告
@SuppressWarnings({“unchecked”, “varargs”}):强制关闭警告弹出(不建议这么做)
The Java™ Tutorials — Generics :Restrictions on Generics 泛型约束
无法利用原始类型来创建泛型解决方法:使用它们的包装类
无法创建类型参数的实例
变通方案:利用反射就是可以
无法创建参数化类型的静态变量
原因:静态变量是类所有,共享决定了其必须能确定。但多个类型作为参数传入此类的多个实例时,就会无法确定究竟赋予此静态变量哪个实例对象所传入的参数了
无法对参数化类型使用转换或者instanceof关键字
但需要注意通配符的情况
无法创建参数化类型的数组
无法创建、捕获或是抛出参数化类型对象
但却可以在throw后使用类型参数
当一个方法的所有重载方法的形参类型擦除后,如果它们具有了相同的原始类型,那么此方法不可重载
原因:此情境下,类型擦除会产生两个同签名的方法
拓展阅读
Can I create an array whose component type is a concrete parameterized type? 我可以创建一个元素类型为具体的参数化类型的数组嘛(我可以创建一个泛型数组嘛)?
泛型数组被禁止的原因:类型擦除后会导致数组元素类型不确定性,举例
Pair<Integer,Integer>[] intPairArr = new Pair<Integer,Integer>[10]; // illegal Object[] objArr = intPairArr; objArr[0] = new Pair<String,String>("",""); // should fail
我们期望intPairArr仅仅存储Pair
Pair[] intPairArr = new Pair[10];
数组仅保留了其原始类型(raw type)。这样一来,数组就会接受任何类型的键值对了,如Pair
Node<?>[] nodes = new Node[10]; //这是可以的
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树