1.简单的泛型类

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
31
class Pair<T>{
private T first;
private T second;
public Pair(){
first = null;
second = null;
}
public Pair(T first, T second){
this.first = first;
this.second = second;
}
public T getFirst(){
return first;
}
public T getSecond(){
return second;
}
public void setFirst(T first){
this.first = first;
}
public void setSecond(T second){
this.second = second;
}
}

 

2.泛型方法的编写

以计算自定义对象数组中的最小元素为例:

1
2
3
4
5
6
7
8
public static <T extends Comparable> T getMin(T[] a){
if(a == null || a.length == 0) return null;
T min = a[0];
for(int i = 0; i < a.length; i++){
if(min.compareTo(a[i]) > 0) min = a[i];
}
return min;
}

<1>要比较自定义对象的大小就必须要定义自定义对象之间的大小关系,才能使用方法compareTo方法进行比较。这就需要保证T所属的类有compareTo方法,可以通过对类型变量T设置限定实现,示例代码中实现的限定类型为Comparable。

<2>一个变量或通配符可以有多个限定,限定类型用“&”分隔,类型变量用“,”分隔,例如:

1
<K extends Comparable & Serializable, V>

3.泛型和虚拟机

虚拟机的一种机制:无论何时定义了一个泛型类型,都自动提供了一个相应的原始类型(raw type)。

这个过程可以理解为擦除类型变量(例如例子中的T),并将其替换成第一个限定类型(例子中的Comparable),没有指定限定类型用Object代替。

也即,上述的计算自定义对象数组中的最小元素的例子的代码的原始类型如下:

1
2
3
4
5
6
7
8
public static Comparable getMin(Comparable[] a){
if(a == null || a.length == 0) return null;
Comparable min = a[0];
for(int i = 0; i < a.length; i++){
if(min.compareTo(a[i]) > 0) min = a[i];
}
return min;
}

 

<1>翻译泛型表达式:

当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换,例如如下上下文中的调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//初始化自定义对象数组
Person[] persons = new Person[5];//数组中也可以是Person类的子类对象
for(int i = 4; i >= 0; i--){
persons[i] = new Person("P" + i);
}
Person minPerson = getMin(persons);//调用
public static <T extends Comparable> T getMin(T[] a){
......
}
class Person implements Comparable{
......
}

例中,getMin方法将返回Object类型,然后将返回的Object类型转换为Person类型。

 

<2>翻译泛型方法:

正常情况直接用限定类型或Object代替T实现擦除

但当遇到类型擦除与多态发生冲突,编译器会生成一个桥方法以支持多态。

4.泛型的约束

<1>不能用基本类型实例化参数,要用其包装类型。(不能用int,用Integer)

<2>运行时类型检查只适用于原始类型。

无法使用下面的语句检测对象是否为Pair 的实例:

1
2
Pair<String> ps = new Pair<String>();
if(ps instanceof Pair<String>) //ERROR

错误信息:Cannot perform instanceof check against parameterized type Pair. Use the form Pair<?> instead since further generic type information will be erased at runtime

同样地,getClass()方法总是返回原始类型:

1
2
3
4
5
Pair<String> ps = new Pair<String>();
Pair<Integer> pi = new Pair<Integer>();
if(ps.getClass() == pi.getClass()) //equall
System.out.println(ps.getClass()); //class Main.Pair
System.out.println(pi.getClass()); //class Main.Pair

<3>不能创建泛型数组

1
Pair<String> table = new Pair<String>[10]; //ERROR

错误信息:Cannot create a generic array of Pair

<4>不能实例化类型变量

1
2
3
4
5
6
7
class Pair<T>{
public Pair(){
first = new T();
second = new T();
}//ERROR
……
}

擦除后构造的不是T,是限定类型或Object,本意肯定不是希望调用new Object(),但可以使用反射调用Class.newInstance方法构造泛型对象。