站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2SE 专题J2SE5.0新特性之范型编程

J2SE5.0新特性之范型编程

摘要: J2SE5.0新特性之范型编程

J2SE5.0新特性之范型编程<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

晁岳攀 smallnest@163.com

本章主要参考sun公司文档。

C++程序员对范型编程肯定不陌生,尤其在STL大行其道的时候,C#2.0也将实现范型编程的功能。Java也不甘示弱,也推出了范型编程的语言新特性。

1.一个简单的范型示例

在以前,你可能遇到过这样的代码:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

List list = new LinkedList();

list.add(“麻省理工”);

list.add(“普林斯顿”);

list.add(“伯克利”);

String name = (String)list.iterator.next();

</td></tr></table>

注意,第三行需要强制转换。而使用范型:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

List list = new LinkedList();

list.add(“麻省理工”);

list.add(“普林斯顿”);

list.add(“伯克利”);

String name = list.iterator.next();

</td></tr></table>

这里将list声明成String类型的List。List是有一个类型参数的范型接口。这个例子中类型参数是String。

2.定义简单的范型

看j2se5.0中List和Iterator接口的实现(片断):

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public interface List

{

void add(E x);

Iterator iterator();

}

public interface Iterator

{

E next();

boolean hasNext();

}

</td></tr></table>

上面的代码我们比较熟悉,但是其中增加了尖括号。尖括号中的内容定义了接口List和Iterator的形式类型参数。类型参数可以用在范型声明中,如类和接口的声明。

一旦声明了范型,你就可以使用它。在上面的例子中使用了List。这里使用String是实参,代替了形参E。如果使用List,则用实参Integer代替了形参E。

不管List还是List,它们的类只有一个。考虑下面的代码:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="HEIGHT: 21.25pt; mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; HEIGHT: 21.25pt; BACKGROUND-COLOR: transparent" vAlign=top width=568>

List list1 = new LinkedList();

List list2 = new LinkedList();

System.out.println(list1.getClass()==list2.getClass());

</td></tr></table>

输出结果为true。

一般来说,形式类型参数都是大写,尽量使用单个字母,许多容器类都使用E作为参数。

3.范型和继承

考虑下面的代码,你认为它会出错吗?

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

String s = “smallnest@163.com”;

Object o = s:

</td></tr></table>

当然,String类继承Object类,这样做不会出错。但下面的代码呢?

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

List s = new LinkedList();

Listo=s;

</td></tr></table>

编译出错!

是的,List和List没有继承关系。

4.通配符

考虑下面一个方法:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public void printCollection(Collection c)

{

for(Object o:c)

{

System.out.printf(“%s%n”,o);

}

}

</td></tr></table>

事实上,上面这个方法并不通用,它只能打印Collection类型的集合,象其他的如Collection、Collection并不能被打印,因为对象类型不一致。

为了解决这个问题,可以使用通配符:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public void printCollection(Collection<?> c)

{

for(Object o:c)

{

System.out.printf(“%s%n”,o);

}

}

</td></tr></table>

Collection<?>被称作未知类型的集合。问号代表各种类型。

上面的读取集合中的数据时,我们采用Object类型。这样做时可以的,因为不管未知类型最终代表何种类型,它的数据都继承Object类,那么再考虑一下下面的代码:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

Collection<?> c = new ArrayList();

c.add(new Object()); //!!!!

</td></tr></table>

这样做时错误的,因为我们不知道?代表何种类型,所以我们不能直接将Object增加到集合中,这会出现类型不匹配的情况。

5.有限制的通配符

考虑下面的代码

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

class Man

{

public String name = “”;

}

class GoodMan extends Man

{

public String name = “”

}

class BadMan extends Man

{

public String name = “”

}

</td></tr></table>

考虑下面的范型方法:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public void printName(List men)

{

for(Man man:men)

{

System.out.println(“姓名:” + man.name);

}

}

</td></tr></table>

这个范型方法只能显示List类型的数据,下面的代码允许显示Man和它的子类。

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public void printName(List<? extends Man> men)

{

for(Man man:men)

{

System.out.println(“姓名:” + man.name);

}

}

</td></tr></table>

这里使用? extends Man代替Man,表明接受任何Man的子类做为参数。

和前面的代码类似,下面的代码也是不正确的:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public void adman(List<? extends Man> men)

{

GoodMan good = new GoodMan();

good.name = “晁岳攀”;

men.add(good);

}

</td></tr></table>

原因也很简单,因为?代表一切继承Man的类,你并不能保证就一定时GoodMan类。

和这种用法类似:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public void adman(List<? super GoodMan> men)

{

GoodMan good = new GoodMan();

good.name = “晁岳攀”;

men.add(good);

}

</td></tr></table>

6.范型方法

考虑下面的代码,我们将一个数组的内容加到一个集合中

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public void copyArrayToCollection(Man[] men, Collection<?>c)

{

for(Man man:men)

{

c.add(man);

}

}

</td></tr></table>

这段代码时错的!

因为我们并不知道集合C的类型,所以不能将Man类型的数据加到集合中。

可以使用范型方法解决:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

public void copyArrayToCollection(T[] men, Collectionc)

{

for(T man:men)

{

c.add(man);

}

}

</td></tr></table>

这里T时一个形式类型参数。

何时该采用通用方法?何时该采用通配符?

考虑下面的例子:

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

interface Collection

{

public boolean containsAll(Collection<?> c);

public boolean addAll(Collection<? extends E> c);

}

</td></tr></table>

改写成通用方法

<table class=MsoTableGrid style="BACKGROUND: #cccccc; BORDER-COLLAPSE: collapse; mso-yfti-tbllook: 480; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellSpacing=0 cellPadding=0 border=0> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"> <td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 5.4pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #d4d0c8; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent" vAlign=top width=568>

interface Collection

{

public boolean containsAll(Collection c);

public boolean addAll(Collection c);

}

</td></tr></table>

然而,在这里每个方法T只使用了一次,返回值不依赖形式参数,其他参数也不依赖形式参数。这说明实参被用作多态,这种情况下就应该用通配符。