您的位置:首页 > 编程语言 > Java开发

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 泛型