考虑例子 Matrix 类,它使用类型参数 V,该参数由 Number 类来限制:
public class Matrix
编译器允许您创建 Matrix
5. 一个简单的泛型类
5.1 编写基本的容器类
此时,您可以开始编写简单的泛型类了。到目前为止,泛型类最常见的用例是容器类(比如集合框架)或者值持有者类(比如 WeakReference 或 ThreadLocal)。我们来编写一个类,它类似于 List,充当一个容器。其中,我们使用泛型来表示这样一个约束,即 Lhist 的所有元素将具有相同类型。为了实现起来简单,Lhist 使用一个固定大小的数组来保存值,并且不接受 null 值。
Lhist 类将具有一个类型参数 V(该参数是 Lhist 中的值的类型),并将具有以下方法:
public class Lhist
public Lhist(int capacity) { ... }
public int size() { ... }
public void add(V value) { ... }
public void remove(V value) { ... }
public V get(int index) { ... }
}
要实例化 Lhist,只要在声明时指定类型参数和想要的容量:
Lhist
5.2 实现构造函数
在实现 Lhist 类时,您将会遇到的第一个拦路石是实现构造函数。您可能会像下面这样实现它:
public class Lhist
private V[] array;
public Lhist(int capacity) {
array = new V[capacity]; // illegal
}
}
这似乎是分配后备数组最自然的一种方式,但是不幸的是,您不能这样做。具体原因很复杂,当学习到底层细节一节中的“擦除”主题时,您就会明白。分配后备数组的实现方式很古怪且违反直觉。下面是构造函数的一种可能的实现(该实现使用集合类所采用的方法):
public class Lhist
private V[] array;
public Lhist(int capacity) {
array = (V[]) new Object[capacity];
}
}
另外,也可以使用反射来实例化数组。但是这样做需要给构造函数传递一个附加的参数 —— 一个类常量,比如 Foo.class。后面在 Class
5.3 实现方法
实现 Lhist 的方法要容易得多。下面是 Lhist 类的完整实现:
public class Lhist
private V[] array;
private int size;
public Lhist(int capacity) {
array = (V[]) new Object[capacity];
}
public void add(V value) {
throw new IndexOutOfBoundsException(Integer.toString(size));
else if (value == null)
throw new NullPointerException();
array[size++] = value;
}
public void remove(V value) {
int removalCount = 0;
for (int i=0; i
++removalCount;
else if (removalCount > 0) {
array[i-removalCount] = array[i];
array[i] = null;
}
}
size -= removalCount;
}
public int size() { return size; }
public V get(int i) {
if (i >= size)
throw new IndexOutOfBoundsException(Integer.toString(i));
return array[i];
}
}
注意,您在将会接受或返回 V 的方法中使用了形式类型参数 V,但是您一点也不知道 V 具有什么样的方法或域,因为这些对泛型代码是不可知的。
5.4 使用 Lhist 类
使用 Lhist 类很容易。要定义一个整数 Lhist,只需要在声明和构造函数中为类型参数提供一个实际值即可:
Lhist
编译器知道,li.get() 返回的任何值都将是 Integer 类型,并且它还强制传递给 li.add() 或 li.remove() 的任何东西都是 Integer。除了实现构造函数的方式很古怪之外,您不需要做任何十分特殊的事情以使 Lhist 是一个泛型类。
6. Java类库中的泛型
6.1 集合类
到目前为止,Java 类库中泛型支持存在最多的地方就是集合框架。就像容器类是 C++ 语言中模板的主要动机一样(参阅附录 A:与 C++ 模板的比较)(尽管它们随后用于很多别的用途),改善集合类的类型安全是 Java 语言中泛型的主要动机。集合类也充当如何使用泛型的模型,因为它们演示了泛型的几乎所有的标准技巧和方言。
所有的标准集合接口都是泛型化的 —— Collection
集合类也使用泛型的许多“技巧”和方言,比如上限通配符和下限通配符。例如,在接口 Collection
interface Collection
boolean addAll(Collection< extends V>);
}
该定义组合了通配符类型参数和有限制类型参数,允许您将 Collection
如果类库将 addAll() 定义为接受 Collection
应该可以将 addAll() 的类型参数定义为 Collection
作为泛型化一个类(如果不小心的话)如何会更改其语义的一个更加微妙的例子,注意 Collection.removeAll() 的参数的类型是 Collection< >,而不是 Collection< extends V>。这是因为传递混合类型的集合给 removeAll() 是可接受的,并且更加限制地定义 removeAll 将会更改方法的语义和有用性。