RSS
热门关键字:  java  Ajax  JSP  JSF  Struts
当前位置 : 首页>Java>列表

Java 理论和实践: 了解泛型

来源: 作者: 时间:2007-11-07 点击:


class Foo {
public void doSomething(Set set) {
Set copy = new HashSet(set);
}
}


这种构造不那么直观,但它是类型安全的,而且可以像 new HashSet(set) 那样工作。

构造数组

如何实现 ArrayList?假设类 ArrayList 管理一个 V 数组,您可能希望用 ArrayList 的构造函数创建一个 V 数组:

class ArrayList {
private V[] backingArray;
public ArrayList() {
backingArray = new V[DEFAULT_SIZE]; // illegal
}
}

但是这段代码不能工作 —— 不能实例化用类型参数表示的类型数组。编译器不知道 V 到底表示什么类型,因此不能实例化 V 数组。

Collections 类通过一种别扭的方法绕过了这个问题,在 Collections 类编译时会产生类型未检查转换的警告。ArrayList 具体实现的构造函数如下:

class ArrayList {
private V[] backingArray;
public ArrayList() {
backingArray = (V[]) new Object[DEFAULT_SIZE];
}
}

为何这些代码在访问 backingArray 时没有产生 ArrayStoreException 呢?无论如何,都不能将 Object 数组赋给 String 数组。因为泛型是通过擦除实现的,backingArray 的类型实际上就是 Object[],因为 Object 代替了 V。这意味着:实际上这个类期望 backingArray 是一个 Object 数组,但是编译器要进行额外的类型检查,以确保它包含 V 类型的对象。所以这种方法很奏效,但是非常别扭,因此不值得效仿(甚至连泛型 Collections 框架的作者都这么说,请参阅参考资料)。

还有一种方法就是声明 backingArray 为 Object 数组,并在使用它的各个地方强制将它转化为 V[]。仍然会看到类型未检查转换警告(与上一种方法一样),但是它使一些未明确的假设更清楚了(比如 backingArray 不应逃避 ArrayList 的实现)。

其他方法

最好的办法是向构造函数传递类文字(Foo.class),这样,该实现就能在运行时知道 T 的值。不采用这种方法的原因在于向后兼容性 —— 新的泛型集合类不能与 Collections 框架以前的版本兼容。

下面的代码中 ArrayList 采用了以下方法:

public class ArrayList implements List {
private V[] backingArray;
private Class elementType;
public ArrayList(Class elementType) {
this.elementType = elementType;
backingArray = (V[]) Array.newInstance(elementType, DEFAULT_LENGTH);
}
}

但是等一等!仍然有不妥的地方,调用 Array.newInstance() 时会引起未经检查的类型转换。为什么呢?同样是由于向后兼容性。Array.newInstance() 的签名是:

public static Object newInstance(Class componentType, int length)


而不是类型安全的:

public static T[] newInstance(Class componentType, int length)


为何 Array 用这种方式进行泛化呢?同样是为了保持向后兼容。要创建基本类型的数组,如 int[],可以使用适当的包装器类中的 TYPE 字段调用 Array.newInstance()(对于 int,可以传递 Integer.TYPE 作为类文字)。用 Class 参数而不是 Class 泛化 Array.newInstance(),对于引用类型有更好的类型安全,但是就不能使用 Array.newInstance() 创建基本类型数组的实例了。也许将来会为引用类型提供新的 newInstance() 版本,这样就两者兼顾了。

在这里可以看到一种模式 —— 与泛型有关的很多问题或者折衷并非来自泛型本身,而是保持和已有代码兼容的要求带来的副作用。

泛化已有的类

在转化现有的库类来使用泛型方面没有多少技巧,但与平常的情况相同,向后兼容性不会凭空而来。我已经讨论了两个例子,其中向后兼容性限制了类库的泛化。

另一种不同的泛化方法可能不存在向后兼容问题,这就是 Collections.toArray(Object[])。传入 toArray() 的数组有两个目的 —— 如果集合足够小,那么可以将其内容直接放在提供的数组中。否则,利用反射(reflection)创建相同类型的新数组来接受结果。如果从头开始重写 Collections 框架,那么很可能传递给 Collections.toArray() 的参数不是一个数组,而是一个类文字:

interface Collection {
public T[] toArray(Class elementClass);
}


因为 Collections 框架作为良好类设计的例子被广泛效仿,但是它的设计受到向后兼容性约束,所以这些地方值得您注意,不要盲目效仿。

首先,常常被混淆的泛型 Collections API 的一个重要方面是 containsAll()、removeAll() 和 retainAll() 的签名。您可能认为 remove() 和 removeAll() 的签名应该是:

共3页: 上一页 [1] 2 [3] 下一页
最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册
Google Adsense
相关文章