WBlog

哀吾生之须臾,羡长江之无穷

0%

《Java核心技术卷Ⅰ(原书12版)》第八章泛型程序设计

8.2 定义简单泛型类

以下是一个Pair类作为例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Pair<T,U> {
private T first;
private U second;

public Pair() {
this.first = null;
this.second = null;
}

public Pair(T first, U second) {
this.first = first;
this.second = second;
}

public T getFirst() {
return first;
}

public void setFirst(T first) {
this.first = first;
}

public U getSecond() {
return second;
}

public void setSecond(U second) {
this.second = second;
}
}

泛型类的指代一般用使用大写字母

  • Java类库使用E表示集合的元素类型
  • K和V分别表示键和值的类型
  • T表示任意类型

8.3 泛型方法

以下是一个写在普通类中的泛型方法:

1
2
3
4
5
6
7
8
9
public class 泛型方法 {
public static void main(String[] args) {
System.out.println(getMiddle(1, 2, 3));
System.out.println(getMiddle("Wwh", "is", "handsome"));
}
public static <T> T getMiddle(T ...a) {
return a[a.length / 2];
}
}

注意,类型变量符放在修饰符后(这个例子中是在static后),并在返回类型前面。

8.4 类型变量的限定

在8.3中撰写的方法,并没有指定对应的类型,所有无论传入什么它都会返回

有时我们需要指定传入的类型的泛型,这时可以使用<T extends Comparable>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class 泛型类型变量限定 {
public static void main(String[] args) {
System.out.println(max(1, 2, 3));
System.out.println(max(1.1, 2.2, 3.3));
}

public static <T extends Comparable> T max(T ...a) {
T largest = a[0];
for (T t : a) {
if (t.compareTo(largest) > 0) {
largest = t;
}
}
return largest;
}
}

上面的例子传入的类型必须是实现了Comparable接口的类型

一个类型变量或通配符可以有多个限定,例如<T extends Comparable & Serializable>

可以根据需要有多个接口超类型,但最多有一个限定可以是类

如果有一个类是限定,它必须是第一个限定

8.5 泛型代码和虚拟机

8.5.1 类型擦除

无论何时定义一个泛型类型,都会自动提供一个相应的原始类型,Pair<T>类一开始是如下的形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Pair {
private Object first;
private Object second;

public Pair() {
this.first = null;
this.second = null;
}

public Pair(Object first, Object second) {
this.first = first;
this.second = second;
}

public Object getFirst() {
return first;
}

public void setFirst(Object first) {
this.first = first;
}

public Object getSecond() {
return second;
}

public void setSecond(Object second) {
this.second = second;
}
}

无论是Pair<String>还是Pair<LocalDateTime>在擦除类型后都是原始的Pair类型

原始类型用第一个限定类型替换类型变量,如果不提供限定,则使用Object替换T

8.6 限制与局限性

8.6.1 不能用基本类型实例化类型参数

如题,在使用时没有Pair<double>,只有Pair<Double>

原因在于类型擦除,Object类型无法存储double

8.6.2 运行时类型检查只适用于原始类型

1
2
3
4
5
6
7
8
9
10
11
12
public class 运行时类型检查只适用于原始类型 {
public static void main(String[] args) {
var a = new Object();
if(a instanceof Pair<String,Integer>) { // Error
System.out.println("a is a Pair<String,Integer>");
}
if(a instanceof Pair<T,T>) { // Error
System.out.println("a is a Pair<?,?>");
}
System.out.println((new Pair<String,Double>().getClass()) == (new Pair<Integer,Double>().getClass()); // true
}
}

对于Pair<T,U>来说,对其适用类型检查只会导致错误

无论TU是什么类型,它们的getClass()方法总会返回相同的值