赛迪网 > IT技术 Java > 技术动态
  IT资讯搜索
 
IT产品搜索
[程序开发][网管世界][网络安全][数据库技术]
[操作系统][嘉宾聊天·在线访谈][活动集锦]
[精彩专题][Symantec专区][订阅IT技术周刊]
[开发论坛][网管论坛][安全论坛][数据库论坛]
[操作系统论坛][Sybase专区][IBM dW技术专区]
[病毒求助][病毒与漏洞播报][文档·源码下载]

J2SE:综合J2SE(TM)5.0专题之语言特性 (2)

发布时间:2006.05.11 08:01     来源:赛迪网论坛    作者:

我们可以看到,在这个简单的例子中,我们在定义aList的时候指明了它是一个直接受
Integer类型的ArrayList,当我们调用aList.get(0)时,我们已经不再需要先显式的将
结果转换成Integer,然后再赋值给myInteger了。而这一步在早先的Java版本中是必须
的。也许你在想,在使用Collection时节约一些类型转换就是Java泛型的全部吗?远不
止。单就这个例子而言,泛型至少还有一个更大的好处,那就是使用了泛型的容器类变
得更加健壮:早先,Collection接口的get()和Iterator接口的next()方法都只能返回
Object类型的结果,我们可以把这个结果强制转换成任何Object的子类,而不会有任何
编译期的错误,但这显然很可能带来严重的运行期错误,因为在代码中确定从某个
Collection中取出的是什么类型的对象完全是调用者自己说了算,而调用者也许并不清
楚放进Collection的对象具体是什么类的;就算知道放进去的对象“应该”是什么类,
也不能保证放到Collection的对象就一定是那个类的实例。现在有了泛型,只要我们定
义的时候指明该Collection接受哪种类型的对象,编译器可以帮我们避免类似的问题溜
到产品中。我们在实际工作中其实已经看到了太多的ClassCastException,不是吗?

 

泛型的使用从这个例子看也是相当易懂。我们在定义ArrayList时,通过类名后面的<>括
号中的值指定这个ArrayList接受的对象类型。在编译的时候,这个ArrayList会被处理
成只接受该类或其子类的对象,于是任何试图将其他类型的对象添加进来的语句都会被
编译器拒绝。

 

那么泛型是怎样定义的呢?看看下面这一段示例代码:(其中用E代替在实际中将会使用
的类名,当然你也可以使用别的名称,习惯上在这里使用大写的E,表示Collection的元
素。)

 

    public class TestGenerics {

        Collection col;

        public void doSth(E elem) {

            col.add(elem);

            // ...

        }

    }

 

在泛型的使用中,有一个很容易有的误解,那就是既然Integer是从Object派生出来的,
那么ArrayList当然就是ArrayList的子类。真的是这样吗?我们仔细
想一想就会发现这样做可能会带来的问题:如果我们可以把ArrayList向上转
型为ArrayList,那么在往这个转了型以后的ArrayList中添加对象的时候,我
们岂不是可以添加任何类型的对象(因为Object是所有对象的公共父类)?这显然让我
们的ArrayList失去了原本的目的。于是Java编译器禁止我们这样做。那既然
是这样,ArrayList以及ArrayList、ArrayList等等有没有
公共的一个版本来代替所有呢?有,那就是ArrayList。?在这里叫做通配符。我们为
了缩小通配符所指代的范围,通常也需要这样写:ArrayList
这样写的含义是定义的是这样一类ArrayList,比方说SomeClass有SomeExtendedClass1
和SomeExtendedClass2这两个子类,那么ArrayList就是如下几
个类型的通配写法:ArrayList、ArrayList
ArrayList

 

接下来我们更进一步:既然ArrayList是一个通配的写法,那么
我们可不可以往声明为ArrayList的ArrayList实例中添加一个
SomeExtendedClass1的对象呢?答案是不能。甚至你不能添加任何对象。为什么?因为
ArrayList实际上代表了所有ArrayList
ArrayList和ArrayList三种ArrayList,甚
至包括未知的接受SomeClass其他子类对象的ArrayList。我们拿到一个定义为
ArrayList的ArrayList的时候,我们并不能确定这个ArrayList
具体是使用哪个类作为参数定义的,因此编译器也无法让这段代码编译通过。举例来
讲,如果我们想往这个ArrayList中放一个SomeExtendedClass2的对象,我们如何保证它
实际上不是其他的如ArrayList,而就是这个
ArrayList呢?(还记得吗?ArrayList并非
ArrayList的子类。)怎么办?我们需要使用泛型方法。泛型方法的定义类似下
面的例子:

 

    public static  void add (Collection c, T elem) {

        c.add(elem);

    }

 

其中T代表了我们这个方法期待的那个最终的具体的类,相关的声明必须放在方法签名的
紧靠返回类型说明之前。在本例中,它可以是SomeClass或者SomeClass的任何子类,其
说明放在void关键字之前(只能放在这里)。这样我们就可以让
编译器确信当我们试图添加一个元素到泛型的ArrayList实例中时,可以保证类型安全。

 

Java泛型的最大特点在于它是在语言级别实现的,区别于C# 2.0中的CLR级别。这样的做
法使得JRE可以不必做大的调整,缺点是无法支持一些运行时的类型甄别。一旦编译,它
就被写死了,能都提供的动态能力相当弱。

 

个人认为泛型是这次J2SE(TM) 5.0中引入的最重要的语言元素,给Java语言带来的影响
也是最大。举个例子来讲,我们可以看到,几乎所有的Collections API都被更新成支持
泛型的版本。这样做带来的好处是显而易见的,那就是减少代码重复(不需要提供多个
版本的某一个类或者接口以支持不同类的对象)以及增强代码的健壮性(编译期的类型
安全检查)。不过如何才能真正利用好这个特性,尤其是如何实现自己的泛型接口或类
供他人使用,就并非那么显而易见了。让我们一起在使用中慢慢积累。

 

 

[增强的for循环]

 

你是否已经厌倦了每次写for循环时都要写上那些机械的代码,尤其当你需要遍历数组或
者Collection,如:(假设在Collection中储存的对象是String类型的)

 

public void showAll (Collection c) {

    for (Iterator iter = c.iterator(); iter.hasNext(); ) {

        System.out.println((String) iter.next());

    }

}

 

public void showAll (String[] sa) {

    for (int i = 0; i < sa.length; i++) {

        System.out.println(sa[i]);

    }

}

 

这样的代码不仅显得臃肿,而且容易出错,我想我们大家在刚开始接触编程时,尤其是
C/C++和Java,可能多少都犯过以下类似错误的一种或几种:把for语句的三个表达式顺
序弄错;第二个表达式逻辑判断不正确(漏掉一些、多出一些、甚至死循环);忘记移
动游标;在循环体内不小心改变了游标的位置等等。为什么不能让编译器帮我们处理这
些细节呢?在5.0中,我们可以这样写:

 

public void showAll (Collection c) {

    for (Object obj : c) {

        System.out.println((String) obj);

    }

}

 

public void showAll (String[] sa) {

    for (String str : sa) {

        System.out.println(str);

    }

}

 

这样的代码显得更加清晰和简洁,不是吗?具体的语法很简单:使用":"分隔开,前面的
部分写明从数组或Collection中将要取出的类型,以及使用的临时变量的名字,后面的
部分写上数组或者Collection的引用。加上泛型,我们甚至可以把第一个方法变得更加
漂亮:

 

public void showAll (Collection cs) {

    for (String str : cs) {

        System.out.println(str);

    }

}

 

有没有发现:当你需要将Collection替换成String[],你所需要做的仅仅是简
单的把参数类型"Collection"替换成"String[]",反过来也是一样,你不完全
需要改其他的东西。这在J2SE(TM) 5.0之前是无法想象的。

 

对于这个看上去相当方便的新语言元素,当你需要在循环体中访问游标的时候,会显得
很别扭:比方说,当我们处理一个链表,需要更新其中某一个元素,或者删除某个元素
等等。这个时候,你无法在循环体内获得你需要的游标信息,于是需要回退到原先的做
法。不过,有了泛型和增强的for循环,我们在大多数情况下已经不用去操心那些烦人的
for循环的表达式和嵌套了。毕竟,我们大部分时间都不会需要去了解游标的具体位置,
我们只需要遍历数组或Collection,对吧?

<<上一页 1 2 3 4 下一页>>


[ 发表评论 ] 字体[  ] [ 打印 ] [ 进入博客 ] [ 进入论坛 ]  [ 推荐给朋友 ]
  相关文章
· 基于AJAX技术的DataGrid控件编程 (05-10) · 用15分钟了解DaoZero:它为你实现DAO接口 (05-10)
· 新手必读:JAVA开发工具安装配置心得 (05-10) · 浅谈Java中final,finalized,finally (05-10)
· Java EE 5通过终审投票—JSR 224技术规范 (05-10) · 使用Java Annotations来管理对象的生命周期 (05-10)
· JavaOne即将召开 Sun将发布Java EE (05-10) · 【开发工具】Eclipse照亮Java众生 (05-09)
· Java基础:提升JSP应用程序七大绝招 (05-09) · 源码提供:数据库连接的一些代码 (05-09)
  客户需求反馈表
* 姓  名:
更多资料  了解方案  认识厂商
* 单位名称:
* 联系电话:
* 电子邮件:
  赛迪推荐  
  手机·资费 ·新品·导购·评测·手机资费·宽带
手机搜索  诺基亚 N73 MOTO Z6
  IT产品 ·笔记本·台式机·服务器·打印·投影
IT产品搜索 
  IT技术 ·开发·网管·安全·数据库·操作系统
  信息化 ·热点·专题·访谈·周刊·方案案例
· 中小企业发展缓慢 电子商务存在的问题多
· 马云追加20亿投资淘宝 图谋电子商务霸主
· 国产ITIL运维先行者 四大厂商角力BI市场
· 企业信息安全解决方案 方正电子公文系统
  IT博客 ·曾剑秋·项立刚·Java学习·网管
  IT技术论坛 ·开发·网管·安全·数据库·系统