Java 泛型
泛型是 JDK 5 中引入的一个新特性,它的本质是将数据类型参数化。
一、泛型
1. 什么是泛型?
首先来看这样一个案例:
在 Java 中,没有泛型的集合将所有对象都当作 object 处理。
这样的做法带来的问题是:缺失了类型检查,导致存取元素时需要进行额外的判断。
我们希望让集合只能存储某一类型的元素,可以这么做:
简单封装
1
2
3
4
5
6
7
8
9
public class StringList {
List list = new ArrayList();
add(String str) {
list.add(str);
}
}这个做法的缺点是通用性不高,每增加一个类型的变量,就需要增加一个集合类
通用封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyList {
String type;
List list = new ArrayList();
MyList(String type) {
this.type = type;
}
add(Object object) {
if (object.getClass() === type) {
list.add(str);
}
}
}相比于上面的方法,泛型能帮助我们更好地解决这一问题。
泛型的本质是将数据类型参数化。
数据类型不再是既定的,而是可以被动态指定。
2. 泛型的好处
- 代码复用,同一套代码可以适用于不同类型
- 类型安全,编译器会根据泛型做类型检查,就像泛型是既定类型一样
二、泛型语法
1. 说明
- 在方法、类、接口中声明类型参数,将类型参数当作真正的数据类型编写程序
- 在使用泛型方法、泛型类、泛型接口时,传入类型实参,类型参数将被传入的数据类型所替代
2. 语法
1 |
|
3. 泛型标记符
可以在方法、类、接口中声明类型参数,使用 泛型标记符 来标识泛型。
类型标记符只是一个标记,它用来代指被传入的任何数据类型。
泛型标记符 可以随意设置,常用标记符如下:
- E:表示集合中的元素
- T:Type
- K:表示键
- V:表示值
- N:表示数值类型
- ?:表示不确定,可以用于指代一切类型
4. 泛型上下限
1 |
|
T 应该是指定类型的子类型,可以把指定类型看作上限。
1 |
|
T 应该是指定类型的父类型,可以把指定类型看作下限。
三、泛型方法
1. 语法
1 |
|
2. 示例
1 |
|
四、泛型类
1. 语法
1 |
|
2. 示例
1 |
|
五、泛型接口
1. 语法
1 |
|
2. 示例
泛型接口
1 |
|
类实现泛型接口
1 |
|
泛型类实现泛型接口
1 |
|
六、泛型方法的重载
泛型允许设定通配符的上限和下限,因此可以在一个类中定义多个相同的方法(只需要它们的类型通配符覆盖范围不重合),从而实现了泛型方法的重载。
1 |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test {
static <T extends Double> void fun(T parameter) {
System.out.println("Double");
}
static <T extends Integer> void fun(T parameter) {
System.out.println("Integer");
}
static <T extends String> void fun(T parameter) {
System.out.println("String");
}
public static void main(String[] args) {
Test.fun(66.6);
Test.fun(123);
Test.fun("123");
}
}
七、擦除与转移
1. 擦除
当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有的泛型信息将被擦除。
2. 转换
当将没有泛型信息的变量赋给具有泛型信息的变量时,并不会发生错误,编译器会提示“未经检查的转换”。
八、泛型支持 - 伪泛型
JDK 从 1.5 开始才支持泛型,为了兼容之前的版本,Java 采用了 “伪泛型” 的策略,具体来说:
编译期检查:记录泛型的具体值,做类型检查
1
2ArrayList<Integer> arrayList = new ArrayList<>();
// arraylist.add("hello"); 报错泛型擦除:在编译成字节码文件时,Java 会进行 “泛型擦除”,
清除泛型声明,即清除
<···>
根据类型参数的上下限,将不定的类型参数直接替换为具体的类型
1
2<T> -> Object
<T extends Car> -> Car
隐式装箱拆箱:为了使泛型类能够接收原始类型的参数,增加隐式装箱拆箱机制
参考
疯狂 Java 讲义