分类: Java
2007-05-20 11:09:53
Java1.5泛型指南中文版(Java1.5 Generic Tutorial)
英文版pdf下载链接:
译者:
目 录
考虑写一个例程来打印一个集合(Collection)中的所有元素。下面是在老的语言中你可能写的代码:
void printCollection(Collection c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
下面是一个使用泛型的幼稚的尝试(使用了新的循环语法):
void printCollection(Collection
for (Object e : c) {
System.out.println(e);
}
}
问题是新版本的用处比老版本小多了。老版本的代码可以使用任何类型的collection作为参数,而新版本则只能使用Collection,我们刚才阐述了,它不是所有类型的collections的父类。
那么什么是各种collections的父类呢?它写作: Collection>(发音为:"collection of unknown"),就是,一个集合,它的元素类型可以匹配任何类型。显然,它被称为通配符。我们可以写:
void printCollection(Collection> c) {
for (Object e : c) {
System.out.println(e);
}
}
现在,我们可以使用任何类型的collection来调用它。注意,我们仍然可以读取c中的元素,其类型是Object。这永远是安全的,因为不管collection的真实类型是什么,它包含的都是objects。但是将任意元素加入到其中不是类型安全的:
Collection> c = new ArrayList
c.add(new Object()); // 编译时错误
因为我们不知道c的元素类型,我们不能向其中添加对象。
add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。唯一的例外是null,它是所有类型的成员。
另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object,因此把get的返回值赋值给一个Object类型的对象或者放在任何希望是Object类型的地方是安全的。
考虑一个简单的画图程序,它可以用来画各种形状,比如矩形和圆形。
为了在程序中表示这些形状,你可以定义下面的类继承结构:
public abstract class Shape {
public abstract void draw(Canvas c);
}
public class Circle extends Shape {
private int x, y, radius;
public void draw(Canvas c) { // ...
}
}
public class Rectangle extends Shape {
private int x, y, width, height;
public void draw(Canvas c) {
// ...
}
}
这些类可以在一个画布(Canvas)上被画出来:
public class Canvas {
public void draw(Shape s) {
s.draw(this);
}
}
所有的图形通常都有很多个形状。假定它们用一个list来表示,Canvas里有一个方法来画出所有的形状会比较方便:
public void drawAll(List
for (Shape s : shapes) {
s.draw(this);
}
}
现在,类型规则导致drawAll()只能使用Shape的list来调用。它不能,比如说对List
public void drawAll(List extends Shape> shapes) { //..}
这里有一处很小但是很重要的不同:我们把类型 List
List extends Shape>是有限制通配符的一个例子。这里?代表一个未知的类型,就像我们前面看到的通配符一样。但是,在这里,我们知道这个未知的类型实际上是Shape的一个子类(它可以是Shape本身或者Shape的子类而不必是extends自Shape)。我们说Shape是这个通配符的上限(upper bound)。
像平常一样,要得到使用通配符的灵活性有些代价。这个代价是,现在像shapes中写入是非法的。比如下面的代码是不允许的:
public void addRectangle(List extends Shape> shapes) {
shapes.add(0, new Rectangle()); // compile-time error!
}
你应该能够指出为什么上面的代码是不允许的。因为shapes.add的第二个参数类型是? extends Shape ——一个Shape未知的子类。因此我们不知道这个类型是什么,我们不知道它是不是Rectangle的父类;它可能是也可能不是一个父类,所以这里传递一个Rectangle不安全。
有限制的通配符正是我们解决DMV给人口普查局传送名单的例子所需要的。我们的例子假定数据用一个姓名(String)到people(用Person或其子类来表示,比如Driver)。Map
再一次,注意形式类型参数的命名习惯——K代表keys,V代表vlaues。
public class Census {
public static void addRegistry(Map<String, ? extends Person> registry) { ...}
}...
Map<String, Driver> allDrivers = ...;
Census.addRegistry(allDrivers);