JDK1.5中引入了对java语言的多种扩展,泛型(generics)即其中之一。
这个教程的目标是向您介绍java的泛型(generic)。你可能熟悉其他语言的泛型,最著名的是C++的模板(templates)。如果这样,你很快就会看到两者的相似之处和重要差异。如果你不熟悉相似的语法结构,那么更好,你可以从头开始而不需要忘记误解。
Generics允许对类型进行抽象(abstract over types)。最常见的例子是集合类型(Container types),Collection的类树中任意一个即是。
下面是那种典型用法:
List myIntList = new LinkedList();// 1
myIntList.add(new Integer(0));// 2
Integer x = (Integer) myIntList.iterator().next();// 3
第3行的类型转换有些烦人。通常情况下,程序员知道一个特定的list里边放的是什么类型的数据。但是,这个类型转换是必须的(essential)。编译器只能保证iterator返回的是Object类型。为了保证对Integer类型变量赋值的类型安全,必须进行类型转换。
当然,这个类型转换不仅仅带来了混乱,它还可能产生一个运行时错误(run time error),因为程序员可能会犯错。
程序员如何才能明确表示他们的意图,把一个list中的内容限制为一个特定的数据类型呢?这是generics背后的核心思想。这是上面程序片断的一个泛型版本:
List<Integer> myIntList = new LinkedList<Integer>(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = myIntList.iterator().next(); // 3
注意变量myIntList的类型声明。它指定这不是一个任意的List,而是一个Integer的List,写作:List<Integer>。我们说List是一个带一个类型参数的泛型接口(a generic interface that takes a type parameter),本例中,类型参数是Integer。我们在创建这个List对象的时候也指定了一个类型参数。
另一个需要注意的是第3行没了类型转换。
现在,你可能认为我们已经成功地去掉了程序里的混乱。我们用第1行的类型参数取代了第3行的类型转换。然而,这里还有个很大的不同。编译器现在能够在编译时检查程序的正确性。当我们说myIntList被声明为List<Integer>类型,这告诉我们无论何时何地使用myIntList变量,编译器保证其中的元素的正确的类型。与之相反,一个类型转换说明程序员认为在那个代码点上它应该是那种类型。
实际结果是,这可以增加可读性和稳定性(robustness),尤其在大型的程序中。
下面是从java.util包中的List接口和Iterator接口的定义中摘录的片断:
public interface List<E> {
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E> {
E next();
boolean hasNext();
}
这些都应该是很熟悉的,除了尖括号中的部分,那是接口List 和 Iterator 中的形式类型参数的声明 (the declarations of the formal type parameters of the interfaces List and Iterator) 。
类型参数在整个类的声明中可用,几乎是所有可是使用其他普通类型的地方(但是有些重要的限制,请参考第7部分)。
(原文:Type parameters can be used throughout the generic declaration, pretty much where you would use ordinary types (though there are some imp
在介绍那一节我们看到了对泛型类型声明List(the generic type declaration List)的调用,如List
你可能想象,List
public interface IntegerList {
void add(Integer x)
Iterator
}
这种直觉可能有帮助,但是也可能导致误解。
它有帮助,因为List
它可能导致误解,因为泛型声明绝不会实际的被这样替换。没有代码的多个拷贝,源码中没有、二进制代码中也没有;磁盘中没有,内存中也没有。如果你是一个C++程序员,你会理解这是和C++模板的很大的区别。
一个泛型类型的声明只被编译一次,并且得到一个class文件,就像普通的class或者interface的声明一样。
类型参数就跟在方法或构造函数中普通的参数一样。就像一个方法有形式参数(formal value parameters)来描述它操作的参数的种类一样,一个泛型声明也有形式类型参数(formal type parameters)。当一个方法被调用,实参(actual arguments)替换形参,方法体被执行。当一个泛型声明被调用,实际类型参数(actual type arguments)取代形式类型参数。
一个命名的习惯:我们推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通的形式参数很容易被区分开来。许多容器类型使用E作为其中元素的类型,就像上面举的例子。在后面的例子中还会有一些其他的命名习惯。
让我们测试一下我们对泛型的理解。下面的代码片断合法么?
List
List
发表评论