Java泛型
Free Talk
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
廖雪峰、菜鸟
什么是泛型?
在讲解什么是泛型之前,我们先观察Java标准库提供的ArrayList,它可以看作“可变长度”的数组,因为用起来比数组更方便。
实际上ArrayList内部就是一个Object[]数组,配合存储一个当前分配的长度,就可以充当“可变数组”:
1  | public class ArrayList {  | 
如果用上述ArrayList存储String类型,会有这么几个缺点:
- 需要强制转型;
 - 不方便,易出错。
 
例如,代码必须这么写:
1  | ArrayList list = new ArrayList();  | 
为了解决这个问题,我们必须把ArrayList变成一种模板:ArrayList<T>,代码如下:
1  | public class ArrayList<T> {  | 
这样一来,我们就实现了:编写一次模版,可以创建任意类型的ArrayList:
1  | // 创建可以存储String的ArrayList:  | 
因此,泛型就是定义一种模板,例如ArrayList<T>,然后在代码中为用到的类创建对应的ArrayList<类型>,由编译器针对类型作检查。
小结
- 
泛型就是编写模板代码来适应任意类型;
 - 
泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查;
 - 
注意泛型的继承关系:可以把
ArrayList<Integer>向上转型为List<Integer>(T不能变!),但不能把ArrayList<Integer>向上转型为ArrayList<Number>(T不能变成父类)。 
使用泛型
使用ArrayList时,如果不定义泛型类型时,泛型类型实际上就是Object。
当我们定义泛型类型<String>后,List<T>的泛型接口变为强类型List<String>:
1  | // 无编译器警告:  | 
编译器如果能自动推断出泛型类型,就可以省略后面的泛型类型。例如,对于下面的代码:
1  | List<Number> list = new ArrayList<Number>();  | 
编译器看到泛型类型List<Number>就可以自动推断出后面的ArrayList<T>的泛型类型必须是ArrayList<Number>,因此,可以把代码简写为:
1  | // 可以省略后面的Number,编译器可以自动推断泛型类型:  | 
泛型接口
**除了ArrayList<T>使用了泛型,还可以在接口中使用泛型。**例如,Arrays.sort(Object[])可以对任意数组进行排序,但待排序的元素必须实现Comparable<T>这个泛型接口:
1  | public interface Comparable<T> {  | 
可以直接对String数组进行排序:
1  | // sort  | 
这是因为String本身已经实现了Comparable<String>接口。如果换成我们自定义的Person类型就会得到ClassCastException,即无法将Person转型为Comparable。我们修改代码,让Person实现Comparable<T>接口:
1  | // sort  | 
通配符
- ? 表示不确定的 Java 类型
 - T (type) 表示具体的一个 Java 类型
 - K V (key value) 分别代表 Java 键值中的Key Value
 - E (element) 代表Element
 
小结
- 
使用泛型时,把泛型参数
<T>替换为需要的class类型,例如:ArrayList<String>,ArrayList<Number>等; - 
可以省略编译器能自动推断出的类型,例如:
List<String> list = new ArrayList<>();; - 
不指定泛型参数类型时,编译器会给出警告,且只能将
<T>视为Object类型; - 
可以在接口中定义泛型类型,实现此接口的类必须实现正确的泛型类型