2024年4月19日发(作者:)

浅谈Java泛型编程

原文地址:/?messageID=154504

我原本想全文翻译Generics in the Java Programming Language,但是功力不够,太耗时

间。于是乎按照原文的框架,翻了一些再加上自己写的一点东西。

第一次写文,如有谬误还清指出~~谢谢!

浅谈Java泛型编程

1 引言

在JDK 1.5中,几个新的特征被引入Java语言。其中之一就是泛型(generics)。

泛型(generics,genericity)又称为“参数类型化(parameterized type)”或“模板

(templates)”,是和继承(inheritance)不同而互补的一种组件复用机制。

继承和泛型的不同之处在于——在一个系统中,继承层次是垂直方向,从抽象到具体,而泛型是水

平方向上的。当运用继承,不同的类型将拥有相同的接口,并获得了多态性;当运用泛型,将拥有

许多不同的类型,并得以相同的算法作用在它们身上。因此,一般说来,当类型与实现方法无关

时,使用泛型;否则,用继承。

泛型技术最直接联想到的用途就是建立容器类型。下面是一个没有使用泛型技术的例子:

List myIntList = new LinkedList();// 1

(new Integer(0));// 2

Integer x = (Integer)or().next();// 3

显然,程序员知道究竟是什么具体类型被放进了myIntList中。但是,第3行的类型转换

(cast)是必不可少的。因为编译器仅仅能保证iterator返回的是Object类型。要想保证将这个

值传给一个Integer类型变量是安全的,就必须类型转换。

除了使代码显得有些混乱外,类型转换更带来了运行时错误的可能性。因为程序员难免会犯错误。

使用了泛型技术,程序员就可以确切地表达他们的意图,并且把myIntList限制为包含一种具体

类型。下面就是前一个例子采用了泛型的代码段:

List myIntList = new LinkedList();// 1

(new Integer(0));// 2

Integer x = or().next();// 3

List指出了这不是一个随意的List,而是一个Integer的List。我们说List是一个带

有类型参数的泛型接口,在这里就是指Integer。

现在,我们在第1行里使用Integer作为类型参数,而不是在第3行里做类型转换。这样,在编

译时刻,编译器就能够检查程序的正确性——无论何时何地,编译器都将保证myIntList的正确

使用。相反地,类型转换仅仅告诉我们——在这里,程序员认为这样做是对的。

采用泛型可以增强代码可读性和健壮性(robustness)。

2 定义泛型

public interface List {

void add(E x);

Iterator iterator();

}

public interface Interator {

E next();

boolean hasNext();

}

这是一段Collection里代码,一个完整的泛型定义。尖括号里的E就是形式类型参数(formal

type parameters)。在泛型定义中,类型参数的用法就像一般具体类型那样。

在引言中,我们看到初始化了一个泛型List——List。在这里,类型参数被赋于实际类

型参数(actual type argument)Integer。

你可以想象List将获得这样的代码:

public interface List {

void add(Integer x);

Iterator< Integer > iterator();

}

和C++中对模板的处理有很大的不同,这里没有第2份副本。Java采用的是拭去法(erasure)

而C++采用的是膨胀法(expansion)。一个泛型定义只被编译一次,只生成一个文件,就像一

般的class和interface一样。

形式类型参数可以不止1个,如:

class Bar < E, D> { …… }

3 通配符

3.1 泛型和子类

下面的这段代码合法么?

List ls = new ArrayList ();// 1

List lo = ls;// 2

假设这两行代码是正确的,那么下面的操作:

(new Object());// 3

String str = (0);// 4

将导致运行时刻错误。通过别名lo存取ls时,我们可以插入任意类型的对象——ls就不再仅仅持

有String了。

Java编译器消除了这种错误发生的可能性。第2行将导致编译时刻错误。

一般地说,如果Foo是Bar的子类,G定义为某种泛型,那么G不是G的子类。

3.2 通配符

如果,我们试图使用泛型的方法编写一个打印Collection内所有元素的函数,要怎么做?

void printCollection (Collection c) {

for (Objcet obj : c) {// jdk 1.5中新增的语法,见5.1

n(obj);

}

}

显然这样是不行的,因为通过3.1我们可以知道——Collection不是任何Collection

的父类。

那么,所有Collection的父类是什么?Collection——未知类型的Collection(collection

of unknown),一个元素可以匹配为任意类型的Collection。“?”被称作通配类型。上述的代

码,可以改写成这样:

void printCollection(Collection c) {

for (Object obj : c) {

n(obj);

}

}

现在,我们可以使用任意类型的Collection作为参数了。注意,在printCollection内,用

Objcet类型访问c的元素是安全的,因为任何一种具体类型都是Object的子类。

但是这样的操作是错误的:

List list = new ArrayList();

(…);// compile-time error!

因为list被定义为List,“?”指代了一个未知类型。(…)无法保证插入的对象类型就是

本文发布于:2024-04-19,感谢您对本站的认可!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:类型泛型参数使用错误

发布评论

评论列表(有0条评论)
    福州电脑网_福州电脑维修_福州电脑之家_福州iThome

    福州电脑网_福州电脑维修_福州电脑之家_福州iThome

    福州电脑维修网(fzithome.com)专业的电脑维修,笔记本维修,上门维修各种电脑,笔记本,平板等,快速上门.电脑知识频道内容覆盖:计算机资讯,电脑基础应用知识,各种电脑故障维修学习,电脑外设产品维修维护,病毒,软件,硬件,常识.

      相关推荐