| « | December 2025 | » | | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | | | |
| 公告 |
| 本博客在此声明所有文章均为转摘,只做资料收集使用。并无其他商业用途。 |
| Blog信息 |
|
blog名称: 日志总数:210 评论数量:205 留言数量:-19 访问次数:931481 建立时间:2007年5月10日 |

| |
|
[J2SE相关]JDK1.5新特性之generic-泛型/类属(2) 文章收藏, 网上资源, 软件技术, 电脑与网络
李小白 发表于 2007/6/6 22:42:09 |
|
这一篇是接着上文继续的,在这里补充说明,虽然我希望以双语写作,但是把英文文档翻译过来后再翻译回去,似乎是件好傻的事情。。。所以这些翻译并精简的文章算是个例外吧。
第一道虎纹: generic -泛型 / 类属(二)
泛型方法
假设我们想把一个数组的元素都放到一个容器类里,下面是第一次尝试:
500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0> static void fromArrayToCollection(Object[] a, Collection < ? > c) 500)this.width=500'>500)this.width=500" border=0> { 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0> for (Object o : a) 500)this.width=500'>500)this.width=500" border=0> { c.add(o); // compile time error 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0> } 500)this.width=500'>500)this.width=500" align=top border=0>} 500)this.width=500'>500)this.width=500" align=top border=0>
现在你应该学会了不去犯初学者的错误,用Collection < Object > 来作为参数,你也许也发现了用 Collection < ? > 也不成,不知道就是不知道,不能放东西进去。
好了,主角登场:泛型方法
500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0> static < T > void fromArrayToCollection(T[] a, Collection < T > c) 500)this.width=500'>500)this.width=500" border=0> { 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0> for (T o : a) 500)this.width=500'>500)this.width=500" border=0> { c.add(o); // correct 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0> } 500)this.width=500'>500)this.width=500" align=top border=0>} 500)this.width=500'>500)this.width=500" align=top border=0>
方法的声明加入了类型参数,在上面这个方法里,如果c 的元素类型是 a 的元素类型的父类,就能成功执行方法。下面这些代码可以帮助你了解一下:
500)this.width=500'>500)this.width=500" align=top border=0> Object[] oa = new Object[ 100 ]; 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>Collection < Object > co = new ArrayList < Object > (); 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>fromArrayToCollection(oa, co); // T inferred to be Object 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0>String[] sa = new String[ 100 ]; 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>Collection < String > cs = new ArrayList < String > (); 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>fromArrayToCollection(sa, cs); // T inferred to be String 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0>fromArrayToCollection(sa, co); // T inferred to be Object 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0>Integer[] ia = new Integer[ 100 ]; 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>Float[] fa = new Float[ 100 ]; 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>Number[] na = new Number[ 100 ]; 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>Collection < Number > cn = new ArrayList < Number > (); 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>fromArrayToCollection(ia, cn); // T inferred to be Number 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0>fromArrayToCollection(fa, cn); // T inferred to be Number 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0>fromArrayToCollection(na, cn); // T inferred to be Number 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0>fromArrayToCollection(na, co); // T inferred to be Object 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0>fromArrayToCollection(na, cs); // compile-time error 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0> 500)this.width=500'>500)this.width=500" align=top border=0>
注意到我们并没有真正的传入一个类型实参,而是由编译器以方法的实际参数对象来推断,它推断出使得这次方法调用成立的类型,并尽可能地特化这个类型。比如说如果T 推断为 Number 依然成立的时候,就不会推断为 Object 。
现在看来,泛型方法和通配符有些共通的地方,使得类属有一定的灵活性。那么什么时候用泛型方法,什么时候用通配符?看看下面的例子:
500)this.width=500'>500)this.width=500" align=top border=0> interface Collection < E > 500)this.width=500'>500)this.width=500" border=0>{ 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0> public boolean containsAll(Collection < ? > c); 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0> public boolean addAll(Collection < ? extends E > c); 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>} 500)this.width=500'>500)this.width=500" align=top border=0>
以及:
500)this.width=500'>500)this.width=500" align=top border=0> interface Collection < E > 500)this.width=500'>500)this.width=500" border=0>{ 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0> public < T > boolean containsAll(Collection < T > c); 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0> public < T extends E > boolean addAll(Collection < T > c); 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0> // 注意类型变量也可以有上限哦~ 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>} 500)this.width=500'>500)this.width=500" align=top border=0>
这两种方式都达成了同样的目的,使得方法有了多态性。然而注意到在每个方法的声明中,T 只出现了一次,这种情况下就应该用通配符,通配符的主要目的就是提供弹性的泛化,而多态方法则用于表达两个或多个参数间的依赖关系,你回过头去看多态方法的第一个例子,是不是这个情况?如果不存在依赖关系需要表达,就不应该用多态方法,因为从可读性上来说,通配符更清晰。
而且有趣的是,它们并非水火不容,反而可以精妙配合,如下:
500)this.width=500'>500)this.width=500" align=top border=0> class Collections 500)this.width=500'>500)this.width=500" border=0>{ 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0> public static < T > void copy(List < T > dest, List < ? extends T > src) 500)this.width=500'>500)this.width=500" border=0>{ 500)this.width=500'>500)this.width=500" border=0> } 500)this.width=500'>500)this.width=500" align=top border=0>500)this.width=500'>500)this.width=500" align=top border=0>} |
|
|