作用
1. 限制类型参数的类型
如要定义一个类IntegerPair来存取元素类型为Integer的对偶,此类可写成如下的形式:
public class IntegerPair<T extends Integer>
{
public IntegerPair() { first = null; second = null; }
public IntegerPair(T first, T second) { this.first = first; this.second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
private T first;
private T second;
}
类型参数后的extends关键字表示T 的上界为Integer类型,编译器在类型擦除阶段会将T替换成Integer,生成的字节码如下所示:
Compiled from "IntegerPair.java"
public class generic.bound.IntegerPair<T extends java.lang.Integer> {
public generic.bound.IntegerPair();
descriptor: ()V
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: aconst_null
6: putfield #2 // Field first:Ljava/lang/Integer;
9: aload_0
10: aconst_null
11: putfield #3 // Field second:Ljava/lang/Integer;
14: return
public generic.bound.IntegerPair(T, T);
descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)V
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #2 // Field first:Ljava/lang/Integer;
9: aload_0
10: aload_2
11: putfield #3 // Field second:Ljava/lang/Integer;
14: return
public T getFirst();
descriptor: ()Ljava/lang/Integer;
Code:
0: aload_0
1: getfield #2 // Field first:Ljava/lang/Integer;
4: areturn
public T getSecond();
descriptor: ()Ljava/lang/Integer;
Code:
0: aload_0
1: getfield #3 // Field second:Ljava/lang/Integer;
4: areturn
public void setFirst(T);
descriptor: (Ljava/lang/Integer;)V
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field first:Ljava/lang/Integer;
5: return
public void setSecond(T);
descriptor: (Ljava/lang/Integer;)V
Code:
0: aload_0
1: aload_1
2: putfield #3 // Field second:Ljava/lang/Integer;
5: return
}
可见,IntegerPair支持类型为Integer及其子类型的元素,故BoundTest不会通过编译。
public class BoundTest {
public static void main(String[] args) {
IntegerPair integerPair = new IntegerPair();
integerPair.setFirst("text");
}
}
由于IntegerPar的类型参数T的界限为Integer,IntegerPair setFirst的签名为(Ljava/lang/Integer;)V,故编译BoundTest时会报错,因为integerPar setFisrt传入的是String类型:
BoundTest.java:12: error: incompatible types: String cannot be converted to Integer
integerPair.setFirst("text");
^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
2.实现通用算法
可以通过类型参数调用界限的方法,如NaturalNumber所示:
public class NaturalNumber<T extends Integer> {
private T n;
public NaturalNumber(T n) { this.n = n; }
public boolean isEven() {
return n.intValue() % 2 == 0;
}
// ...
}
由于类型参数T的界限为Integer,故可在T上调用Integer的方法,如initValue. 能调用界限的方法是实现通用算法的关键。考虑求某个列表中大于某个元素的算法:
public static <T> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e > elem) // compiler error
++count;
return count;
}
由于运算符>只能在基础数据类型如short, int, long, double, float, byte和char上进行操作,无法对Object进行>操作,故编译器会报错:
GenericAlgorithm.java:19: error: bad operand types for binary operator '>'
if (e > elem) // compiler error
^
first type: T
second type: T
where T is a type-variable:
T extends Object declared in method <T>countGreaterThan(T[],T)
1 error
这个问题会导致countGreaterThan方法不通用。给类型参数加上一个界限,可以很好的解决这个问题:
public interface Comparable<T> {
public int compareTo(T o);
}
新的通用算法如下:
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e.compareTo(elem) > 0)
++count;
return count;
}
多界限
一个类型参数可有多个界限,表达方式如下:
<T extends B1 & B2 & B3>
类型变量T的类型为界限列表中任一类型的子类.zhu’ru注意:如类型参数中的某个界限为类,必须放在界限列表中的第一个位置,如MultiBounds.java所示:
public class MultiBounds {
static class A {
public void a() {
}
}
interface B {
void b();
}
interface C {
void c();
}
static class D <T extends A & B & C> {
public void d(T t) {
t.a();
t.b();
t.c();
}
}
public static void main(String[] args) {
D d = new D();
d.d(new A());
}
}
若D中类型参数的界限类表声明成如下形式,B为接口,类A放在界限列表的第二个位置,故编译器将会报错:
D <T extends B & A & C>
小结
类型参数的界限可以用来限制类型变量的类型,是实现通用算法的关键;类型变量可以有多个界限。