当前页面: 开发资料首页 → J2SE 专题 → J2SE5.0 实例---泛型
摘要: J2SE5.0 实例---泛型
泛型其实并不是一种新的语言元素,C++中早就就有,但是在C++之后的java却没有吸收这个特性,现在Java也有了泛型的特性,大概也和.Net的竞争有关系吧。
首先看泛型的一个应用。
在过去,我们可能经常要写一些类似这样的代码:
List stringList=new LinkedList();
stringList.add("firstString");
stringList.add("secondString");
String str=(String)stringList.iterator().next();
实际上第三行对String的类型转换意义并不大,因为通常我们如果在操作一个List,都是知道这个List里面放的是什么类型对象的,但是我们如果不这样写又通不过语法检查。
利用java的泛型机制,我们可以这么写:
String str=stringList.iterator().next();
这样做的好处是在定义容器的时候就指明了容器中的类型,一方面我们不再需要取一个元素时候做强制类型转换,另外一方面如果在这个容器中放入的对象类型不符合要求,那么会在编译时候产生一个错误,而不是在运行时候才抛出一个异常。
另外这样也提高了程序的可读性。
下面是一个简单的使用泛型类的定义:
public class MyGenericClass {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
值得注意的一点是,静态变量不能够使用泛型定义,也就是说类似下面的语句是非法的:
public static T value;//错误的定义
此外,泛型的定义不会被继承,举个例子来说,如果A是B的子类,而C是一个声明了泛型定义的类型的话,C不是C的子类。为了更好的说明,可以看下面的代码,这段代码是错误的。
List strList =new ArrayList();
List objList=strList; //错误的赋值</td></tr></table>不过这样一段代码是正确的:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">List strList =new ArrayList();strList.add("a string");</td></tr></table>统配类型假设我们需要这样一个函数,使用它可以把一个集合中所有的元素打印出来,在以前我们可能这样定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">void printCollection(Collection c) { Iterator i = c.iterator(); for (k = 0; k < c.size(); k++) { System.out.println(i.next()); }}</td></tr></table>使用新的泛型特性我们可以这样写:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">void printCollection(Collection c) { for (Object e : c) { System.out.println(e); }}</td></tr></table>但是这样有一个问题,假如我们现在有个对象类型是Collection,那么我们不能够将它作为参数传给printCollection,因为Collection并不是Collection的子类。为了解决这个问题,我们可以使用统配类型?,也就是定义成下面这个样子:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">void printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); }}</td></tr></table>可以说Collection<?>是所有Collection的父类。再来看一段下面的代码<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void clearAllMaps(Collection c){ for(Map m:c) { m.clear(); }}</td></tr></table>毫无疑问,它也存在上面我们所说的问题,也就是对HashMap之类Map的子类无法进行操作,但是如果我们将参数改成Collection<?>又不大合理,因为我们只希望对父类为Map的子类进行操作,那么我们可以这样改写:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void clearAllMaps(Collection<? extends Map> c){ for(Map m:c) { m.clear(); }}</td></tr></table>类似于? extends Map之类的统配符称为限定统配类型。假设一个对象h类型为Collection,那么我们将h作为参数传给clearAllMaps,如下面一段代码所示:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">List> h=new ArrayList>();HashMap m=new HashMap();m.put("key","value");h.add(m);clearAllMaps(h);</td></tr></table>对于在类似于上面所说,使用了? extend XXX的方法,值得注意的一点是不能够在方法体内用XXX的子类对象作为代替。如下面一段代码是错误的:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">public void addRectangle(List<? extends Shape> shapes) { shapes.add(0, new Rectangle()); // 错误用法!}</td></tr></table>这里我们假设Rectangle是Shape的一个子类。不允许这样写的原因比较简单,因为调用该方法时候参数类型可能是Shape的另外一个子类。假如说Shape除了Rectangle这个子类以外还有另外一个子类Circle,那么我们可以把一个List类型的对象作为参数传给这个方法(注意这样是合法的),而在方法体内却把一个Rectangle对象放到了shapes里面,这显然是不合理的。除了extends,在泛型参数类型中还可以使用super关键字,参照下面一段程序:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void addString(Collection <? super String> c){ c.add("a String");}</td></tr></table>泛型函数我们在前面提到了统配类型,现在让我们来设想一个函数,它实现这样的功能,将一个数组中的元素添加到一个Collection中,为了保证程序的通用性,我们可能会写出另外一段错误的代码:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void fromArrayToCollection(Object[] a, Collection<?> c) { for (Object o : a) { c.add(o); // 错误的代码 }}</td></tr></table>那么这个函数应该怎么写呢?我们可以通过对函数添加泛型参数的方法实现,如下面所示:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void exfromArrayToCollection(T[] a, Collection c) { for (T o : a) { c.add(o); //这样是正确的 }}</td></tr></table>那么,在什么时候我们应该使用统配类型,什么时候我们应该使用泛型函数呢?答案是取决于函数参数之间,函数参数和返回值之间的类型依赖性。如果一个函数的参数类型与函数返回的参数没有必然关联,同时对于该函数其他的参数的类型也没有依赖关系,那么我们就应该使用统配符,否则就应该使用泛型函数。为了更清楚地说明这一点,我们可以看一下java.util包中Collections类型几个方法的定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">class Collections { static void swap(List<?> list, int i, int j) {...} static void copy (List<? super T> dest, List<? extends T> src) {...}}</td></tr></table>其中swap函数实际上也可以这样定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">static void swap(List list, int i, int j) {...}</td></tr></table>但是注意到这里泛型类型参数T只在参数中用到了一次,也就是说它和函数其他部分没有依赖性,这可以看作是我们应该使用?的一个标志。copy方法中,拷贝源src中的元素必须是dest所能够接受的,src中的元素必须是T的一个子类,但是具体它是哪种子类我们又不必关心,所以方法中使用了泛型作为一个类型参数,同时也用了统配类型作为第二类型参数 ↑返回目录 前一篇: J2SE 5.0实例---原生类型的autoboxing和auto-unboxing 后一篇: [j2se]哈西表和枚举器(转)
不过这样一段代码是正确的:
List strList =new ArrayList();strList.add("a string");</td></tr></table>统配类型假设我们需要这样一个函数,使用它可以把一个集合中所有的元素打印出来,在以前我们可能这样定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">void printCollection(Collection c) { Iterator i = c.iterator(); for (k = 0; k < c.size(); k++) { System.out.println(i.next()); }}</td></tr></table>使用新的泛型特性我们可以这样写:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">void printCollection(Collection c) { for (Object e : c) { System.out.println(e); }}</td></tr></table>但是这样有一个问题,假如我们现在有个对象类型是Collection,那么我们不能够将它作为参数传给printCollection,因为Collection并不是Collection的子类。为了解决这个问题,我们可以使用统配类型?,也就是定义成下面这个样子:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">void printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); }}</td></tr></table>可以说Collection<?>是所有Collection的父类。再来看一段下面的代码<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void clearAllMaps(Collection c){ for(Map m:c) { m.clear(); }}</td></tr></table>毫无疑问,它也存在上面我们所说的问题,也就是对HashMap之类Map的子类无法进行操作,但是如果我们将参数改成Collection<?>又不大合理,因为我们只希望对父类为Map的子类进行操作,那么我们可以这样改写:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void clearAllMaps(Collection<? extends Map> c){ for(Map m:c) { m.clear(); }}</td></tr></table>类似于? extends Map之类的统配符称为限定统配类型。假设一个对象h类型为Collection,那么我们将h作为参数传给clearAllMaps,如下面一段代码所示:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">List> h=new ArrayList>();HashMap m=new HashMap();m.put("key","value");h.add(m);clearAllMaps(h);</td></tr></table>对于在类似于上面所说,使用了? extend XXX的方法,值得注意的一点是不能够在方法体内用XXX的子类对象作为代替。如下面一段代码是错误的:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">public void addRectangle(List<? extends Shape> shapes) { shapes.add(0, new Rectangle()); // 错误用法!}</td></tr></table>这里我们假设Rectangle是Shape的一个子类。不允许这样写的原因比较简单,因为调用该方法时候参数类型可能是Shape的另外一个子类。假如说Shape除了Rectangle这个子类以外还有另外一个子类Circle,那么我们可以把一个List类型的对象作为参数传给这个方法(注意这样是合法的),而在方法体内却把一个Rectangle对象放到了shapes里面,这显然是不合理的。除了extends,在泛型参数类型中还可以使用super关键字,参照下面一段程序:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void addString(Collection <? super String> c){ c.add("a String");}</td></tr></table>泛型函数我们在前面提到了统配类型,现在让我们来设想一个函数,它实现这样的功能,将一个数组中的元素添加到一个Collection中,为了保证程序的通用性,我们可能会写出另外一段错误的代码:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void fromArrayToCollection(Object[] a, Collection<?> c) { for (Object o : a) { c.add(o); // 错误的代码 }}</td></tr></table>那么这个函数应该怎么写呢?我们可以通过对函数添加泛型参数的方法实现,如下面所示:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void exfromArrayToCollection(T[] a, Collection c) { for (T o : a) { c.add(o); //这样是正确的 }}</td></tr></table>那么,在什么时候我们应该使用统配类型,什么时候我们应该使用泛型函数呢?答案是取决于函数参数之间,函数参数和返回值之间的类型依赖性。如果一个函数的参数类型与函数返回的参数没有必然关联,同时对于该函数其他的参数的类型也没有依赖关系,那么我们就应该使用统配符,否则就应该使用泛型函数。为了更清楚地说明这一点,我们可以看一下java.util包中Collections类型几个方法的定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">class Collections { static void swap(List<?> list, int i, int j) {...} static void copy (List<? super T> dest, List<? extends T> src) {...}}</td></tr></table>其中swap函数实际上也可以这样定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">static void swap(List list, int i, int j) {...}</td></tr></table>但是注意到这里泛型类型参数T只在参数中用到了一次,也就是说它和函数其他部分没有依赖性,这可以看作是我们应该使用?的一个标志。copy方法中,拷贝源src中的元素必须是dest所能够接受的,src中的元素必须是T的一个子类,但是具体它是哪种子类我们又不必关心,所以方法中使用了泛型作为一个类型参数,同时也用了统配类型作为第二类型参数 ↑返回目录 前一篇: J2SE 5.0实例---原生类型的autoboxing和auto-unboxing 后一篇: [j2se]哈西表和枚举器(转)
strList.add("a string");
假设我们需要这样一个函数,使用它可以把一个集合中所有的元素打印出来,在以前我们可能这样定义:
void printCollection(Collection c) {
Iterator i = c.iterator();
for (k = 0; k < c.size(); k++)
{
System.out.println(i.next());
使用新的泛型特性我们可以这样写:
void printCollection(Collection c) { for (Object e : c) { System.out.println(e); }}</td></tr></table>但是这样有一个问题,假如我们现在有个对象类型是Collection,那么我们不能够将它作为参数传给printCollection,因为Collection并不是Collection的子类。为了解决这个问题,我们可以使用统配类型?,也就是定义成下面这个样子:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">void printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); }}</td></tr></table>可以说Collection<?>是所有Collection的父类。再来看一段下面的代码<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void clearAllMaps(Collection c){ for(Map m:c) { m.clear(); }}</td></tr></table>毫无疑问,它也存在上面我们所说的问题,也就是对HashMap之类Map的子类无法进行操作,但是如果我们将参数改成Collection<?>又不大合理,因为我们只希望对父类为Map的子类进行操作,那么我们可以这样改写:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void clearAllMaps(Collection<? extends Map> c){ for(Map m:c) { m.clear(); }}</td></tr></table>类似于? extends Map之类的统配符称为限定统配类型。假设一个对象h类型为Collection,那么我们将h作为参数传给clearAllMaps,如下面一段代码所示:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">List> h=new ArrayList>();HashMap m=new HashMap();m.put("key","value");h.add(m);clearAllMaps(h);</td></tr></table>对于在类似于上面所说,使用了? extend XXX的方法,值得注意的一点是不能够在方法体内用XXX的子类对象作为代替。如下面一段代码是错误的:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">public void addRectangle(List<? extends Shape> shapes) { shapes.add(0, new Rectangle()); // 错误用法!}</td></tr></table>这里我们假设Rectangle是Shape的一个子类。不允许这样写的原因比较简单,因为调用该方法时候参数类型可能是Shape的另外一个子类。假如说Shape除了Rectangle这个子类以外还有另外一个子类Circle,那么我们可以把一个List类型的对象作为参数传给这个方法(注意这样是合法的),而在方法体内却把一个Rectangle对象放到了shapes里面,这显然是不合理的。除了extends,在泛型参数类型中还可以使用super关键字,参照下面一段程序:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void addString(Collection <? super String> c){ c.add("a String");}</td></tr></table>泛型函数我们在前面提到了统配类型,现在让我们来设想一个函数,它实现这样的功能,将一个数组中的元素添加到一个Collection中,为了保证程序的通用性,我们可能会写出另外一段错误的代码:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void fromArrayToCollection(Object[] a, Collection<?> c) { for (Object o : a) { c.add(o); // 错误的代码 }}</td></tr></table>那么这个函数应该怎么写呢?我们可以通过对函数添加泛型参数的方法实现,如下面所示:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void exfromArrayToCollection(T[] a, Collection c) { for (T o : a) { c.add(o); //这样是正确的 }}</td></tr></table>那么,在什么时候我们应该使用统配类型,什么时候我们应该使用泛型函数呢?答案是取决于函数参数之间,函数参数和返回值之间的类型依赖性。如果一个函数的参数类型与函数返回的参数没有必然关联,同时对于该函数其他的参数的类型也没有依赖关系,那么我们就应该使用统配符,否则就应该使用泛型函数。为了更清楚地说明这一点,我们可以看一下java.util包中Collections类型几个方法的定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">class Collections { static void swap(List<?> list, int i, int j) {...} static void copy (List<? super T> dest, List<? extends T> src) {...}}</td></tr></table>其中swap函数实际上也可以这样定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">static void swap(List list, int i, int j) {...}</td></tr></table>但是注意到这里泛型类型参数T只在参数中用到了一次,也就是说它和函数其他部分没有依赖性,这可以看作是我们应该使用?的一个标志。copy方法中,拷贝源src中的元素必须是dest所能够接受的,src中的元素必须是T的一个子类,但是具体它是哪种子类我们又不必关心,所以方法中使用了泛型作为一个类型参数,同时也用了统配类型作为第二类型参数 ↑返回目录 前一篇: J2SE 5.0实例---原生类型的autoboxing和auto-unboxing 后一篇: [j2se]哈西表和枚举器(转)
for (Object e : c)
System.out.println(e);
但是这样有一个问题,假如我们现在有个对象类型是Collection,那么我们不能够将它作为参数传给printCollection,因为Collection并不是Collection的子类。为了解决这个问题,我们可以使用统配类型?,也就是定义成下面这个样子:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">void printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); }}</td></tr></table>可以说Collection<?>是所有Collection的父类。再来看一段下面的代码<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void clearAllMaps(Collection c){ for(Map m:c) { m.clear(); }}</td></tr></table>毫无疑问,它也存在上面我们所说的问题,也就是对HashMap之类Map的子类无法进行操作,但是如果我们将参数改成Collection<?>又不大合理,因为我们只希望对父类为Map的子类进行操作,那么我们可以这样改写:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void clearAllMaps(Collection<? extends Map> c){ for(Map m:c) { m.clear(); }}</td></tr></table>类似于? extends Map之类的统配符称为限定统配类型。假设一个对象h类型为Collection,那么我们将h作为参数传给clearAllMaps,如下面一段代码所示:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">List> h=new ArrayList>();HashMap m=new HashMap();m.put("key","value");h.add(m);clearAllMaps(h);</td></tr></table>对于在类似于上面所说,使用了? extend XXX的方法,值得注意的一点是不能够在方法体内用XXX的子类对象作为代替。如下面一段代码是错误的:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">public void addRectangle(List<? extends Shape> shapes) { shapes.add(0, new Rectangle()); // 错误用法!}</td></tr></table>这里我们假设Rectangle是Shape的一个子类。不允许这样写的原因比较简单,因为调用该方法时候参数类型可能是Shape的另外一个子类。假如说Shape除了Rectangle这个子类以外还有另外一个子类Circle,那么我们可以把一个List类型的对象作为参数传给这个方法(注意这样是合法的),而在方法体内却把一个Rectangle对象放到了shapes里面,这显然是不合理的。除了extends,在泛型参数类型中还可以使用super关键字,参照下面一段程序:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void addString(Collection <? super String> c){ c.add("a String");}</td></tr></table>泛型函数我们在前面提到了统配类型,现在让我们来设想一个函数,它实现这样的功能,将一个数组中的元素添加到一个Collection中,为了保证程序的通用性,我们可能会写出另外一段错误的代码:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void fromArrayToCollection(Object[] a, Collection<?> c) { for (Object o : a) { c.add(o); // 错误的代码 }}</td></tr></table>那么这个函数应该怎么写呢?我们可以通过对函数添加泛型参数的方法实现,如下面所示:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">private void exfromArrayToCollection(T[] a, Collection c) { for (T o : a) { c.add(o); //这样是正确的 }}</td></tr></table>那么,在什么时候我们应该使用统配类型,什么时候我们应该使用泛型函数呢?答案是取决于函数参数之间,函数参数和返回值之间的类型依赖性。如果一个函数的参数类型与函数返回的参数没有必然关联,同时对于该函数其他的参数的类型也没有依赖关系,那么我们就应该使用统配符,否则就应该使用泛型函数。为了更清楚地说明这一点,我们可以看一下java.util包中Collections类型几个方法的定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">class Collections { static void swap(List<?> list, int i, int j) {...} static void copy (List<? super T> dest, List<? extends T> src) {...}}</td></tr></table>其中swap函数实际上也可以这样定义:<table class="MsoTableGrid" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: silver; MARGIN: auto auto auto 59.4pt; BORDER-LEFT: medium none; WIDTH: 378pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 480; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext" cellspacing="0" cellpadding="0" width="504" border="1"><tr style="mso-yfti-irow: 0; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 378pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; BACKGROUND-COLOR: transparent; mso-border-alt: solid windowtext .5pt" valign="top" width="504">static void swap(List list, int i, int j) {...}</td></tr></table>但是注意到这里泛型类型参数T只在参数中用到了一次,也就是说它和函数其他部分没有依赖性,这可以看作是我们应该使用?的一个标志。copy方法中,拷贝源src中的元素必须是dest所能够接受的,src中的元素必须是T的一个子类,但是具体它是哪种子类我们又不必关心,所以方法中使用了泛型作为一个类型参数,同时也用了统配类型作为第二类型参数 ↑返回目录 前一篇: J2SE 5.0实例---原生类型的autoboxing和auto-unboxing 后一篇: [j2se]哈西表和枚举器(转)
为了解决这个问题,我们可以使用统配类型?,也就是定义成下面这个样子:
void printCollection(Collection<?> c)
可以说Collection<?>是所有Collection的父类。
再来看一段下面的代码
private void clearAllMaps(Collection c)
for(Map m:c)
m.clear();
毫无疑问,它也存在上面我们所说的问题,也就是对HashMap之类Map的子类无法进行操作,但是如果我们将参数改成Collection<?>又不大合理,因为我们只希望对父类为Map的子类进行操作,那么我们可以这样改写:
private void clearAllMaps(Collection<? extends Map> c)
类似于? extends Map之类的统配符称为限定统配类型。
假设一个对象h类型为Collection,那么我们将h作为参数传给clearAllMaps,如下面一段代码所示:
List> h=new ArrayList>();
HashMap m=new HashMap();
m.put("key","value");
h.add(m);
clearAllMaps(h);
对于在类似于上面所说,使用了? extend XXX的方法,值得注意的一点是不能够在方法体内用XXX的子类对象作为代替。如下面一段代码是错误的:
public void addRectangle(List<? extends Shape> shapes)
shapes.add(0, new Rectangle()); // 错误用法!
这里我们假设Rectangle是Shape的一个子类。
不允许这样写的原因比较简单,因为调用该方法时候参数类型可能是Shape的另外一个子类。假如说Shape除了Rectangle这个子类以外还有另外一个子类Circle,那么我们可以把一个List类型的对象作为参数传给这个方法(注意这样是合法的),而在方法体内却把一个Rectangle对象放到了shapes里面,这显然是不合理的。
除了extends,在泛型参数类型中还可以使用super关键字,参照下面一段程序:
private void addString(Collection <? super String> c)
c.add("a String");
我们在前面提到了统配类型,现在让我们来设想一个函数,它实现这样的功能,将一个数组中的元素添加到一个Collection中,为了保证程序的通用性,我们可能会写出另外一段错误的代码:
private void fromArrayToCollection(Object[] a, Collection<?> c)
for (Object o : a)
c.add(o); // 错误的代码
那么这个函数应该怎么写呢?我们可以通过对函数添加泛型参数的方法实现,如下面所示:
private void exfromArrayToCollection(T[] a, Collection c)
for (T o : a)
c.add(o); //这样是正确的
那么,在什么时候我们应该使用统配类型,什么时候我们应该使用泛型函数呢?答案是取决于函数参数之间,函数参数和返回值之间的类型依赖性。
如果一个函数的参数类型与函数返回的参数没有必然关联,同时对于该函数其他的参数的类型也没有依赖关系,那么我们就应该使用统配符,否则就应该使用泛型函数。
为了更清楚地说明这一点,我们可以看一下java.util包中Collections类型几个方法的定义:
class Collections {
static void swap(List<?> list, int i, int j) {...}
static void copy (List<? super T> dest, List<? extends T> src) {...}
其中swap函数实际上也可以这样定义:
static void swap(List list, int i, int j) {...}
但是注意到这里泛型类型参数T只在参数中用到了一次,也就是说它和函数其他部分没有依赖性,这可以看作是我们应该使用?的一个标志。
copy方法中,拷贝源src中的元素必须是dest所能够接受的,src中的元素必须是T的一个子类,但是具体它是哪种子类我们又不必关心,所以方法中使用了泛型作为一个类型参数,同时也用了统配类型作为第二类型参数
↑返回目录 前一篇: J2SE 5.0实例---原生类型的autoboxing和auto-unboxing 后一篇: [j2se]哈西表和枚举器(转)