求索

Make it work, make it right, make it fast and make it open。
posts - 8, comments - 11, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

关于Generics的一点儿理解

Posted on 2005-07-06 15:57 Java求索 阅读(915) 评论(5)  编辑  收藏 所属分类: Java

JDK5.0已经release很久了,但一直没机会好好学习一下,今天可有机会了。

先来看一段代码:

public class TestDate {

    
public static void main(String[] args) {
        Date date 
= new Date();
        Object 
object = new Object();
        Timestamp stamp 
= new Timestamp(date.getTime());
        System.
out.println("date&stamp:" + date.compareTo(stamp));
        System.
out.println("stamp&date:" + stamp.compareTo(date));
        System.
out.println("date&object:" + date.compareTo(object));
    }
}


这段代码看上去很普通,但是如果用1.4和1.5分别编译就会出现不同的结果。先来说用1.4编译的情况:首先用1.4编译,编译器不会报错,如果运行的话,前面两个输出语句会分别打印“0,0”,而第三个会throw一个ClassCast exception. 因为Date不能与object比较,但是为什么能编译通过呢?察看JDK源代码就可以知道了,Date实现了Comparable接口,这个接口中的CompareTo()方法的参数就是Object。所以Date也不得不有一个以Object为参数的CompareTo()方法,但是这个方法是没有意义的,Date应该与Date比较,所以Date这个Class里面就出现了两个ComparaTo方法,一个是以Date为参数,另一个是以Object为参数,这是1.4以前,不得不采用的方法。不然Date就没法实现Comparable接口了。

JDK1.5中Generics的出现解决了这个问题,如果看1.5中Date类的源代码的话,就会发现它只有一个CompareTo()方法了,那它怎么来实现Comparable接口呢,这就是Generics的功劳了。在Date声明时,实现Comparable接口是这么写的:...Comparable...,并且Comparable接口的声明是这样的:Comparable。这个T代表Type。它可以是任何Object。Comparable的实现类只要说明T是什么具体类型就可以了。因此,Date就可以只有一个CompareTo()方法,又可以实现Comparable接口了。如果用1.5编译上面的Code的话,就会发现这段Code是不能编译通过的,编译器会提示“Severity Description Resource In Folder Location Creation Time 2 The method compareTo(Date) in the type Date is not applicable for the arguments (Object)”,这就避免了1.4中出现的问题。我想如果使用了1.5以后咱们编写代码时,出现ClassCastException的几率就会很小了,因为编译器会替你发现这样的错误。

这就是Generics的好处了。

但是还有一点值得考虑,如果我们去掉错误的那一行代码,在1.5中编译然后运行,会发现还有地方与1.4的不同。第一行输出语句会打印1,而不是0,这说明1.5认为具有相同时间的timestamp和date是不同的,但1.4认为它们相同。我有看了一下1.5和1.4的源代码,发现它们CompareTo(Date ...)的实现方法是不一样的,可能问题就出现在这里。我没有试着去读它的代码,等有时间,一定好好研究一下。

最后,还有一个问题,如果用1.5编译并运行,会throw一个ClassCastException,而用1.4则不会出现这个问题。我想这是因为timestamp继承了Date的CompareTo()方法,所以一个timestamp就可以与Date比较了,但是应用了Generics以后这种情况是不允许的,而且代码也没有特殊处理,因此就会有Exception了。看来Generics也会带来一些其他的问题。

我想这种情况是可以避免的,原则就是只比较具有相同类型的两个对象,而不与其父类或子类比较。如果必须比较的话,也应该用相应的方法转化为相同的类,再进行比较。


评论

# re: 关于Generics的一点儿理解  回复  更多评论   

2005-07-06 18:27 by emu
这是jdk1.5一个已经报告的bug:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103041


问题在于在jdk1.5中Timestamp多定义了一个比较方法:

public int compareTo(java.util.Date o) {
return compareTo((Timestamp)o);
}

这样如楼主所说,在1.4中Timestamp.compareTo(Date)的时候会调用继承来的Date.compareTo(Date)方法来完成比较,而Date.compareTo(Date)在比较前会把参数造型成Date对象,因此可以完成比较;而在1.5中Timestamp则直接使用自己的Timestamp.compareTo(Date)方法来比较,并试图在其中把参数造型成Timestamp,因此抛出造型异常。

但是这并不像楼主想像的,“Generics也会带来一些其他的问题”,而是sun有意为之。看上面这个compareTo方法的相关说明:

// This forwarding method ensures that the compareTo(Date) method defined
// in java.util.Date is not invoked on a Timestamp

显然sun认为1.4中的做法是不对的,下定决心在以放弃兼容性为代价在1.5中更正这个错误,而不是一个bug(真的要修复这个bug的话只要把上面这个方法删掉就行了)。sun坚持的正好就是楼主说的原则问题:只比较具有相同类型的两个对象

放弃兼容性的代价就是有一些在1.4下面正常的代码在1.5下面不能允许了,包括大名鼎鼎的JIRA。对此JIRA的反应是:

JDK 1.5 has a bug that prevents JIRA from processing mail correctly. Until the issue is resolved, JIRA does not support JDK 1.5. We recommend you use JDK 1.4.x but it is possible to continue with JDK 1.5 by restarting the server with option \'-Dallow.jdk.1.5=true\'. You can find the JDK bug at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103041


可是JIRA想要等“issue is resolved”那一天恐怕等不着了,这是个原则问题嘛 :)

# re: 关于Generics的一点儿理解  回复  更多评论   

2005-07-06 23:31 by Java求索
多谢您的评论,我所说的其他问题所指的是Migration的问题。如果一个项目使用了1.4中的compareTo()方法,那么当这个项目要Migrate到1.5的时候,代码就不能编译通过了,必须修改源代码才行。

# re: 关于Generics的一点儿理解  回复  更多评论   

2005-07-07 14:19 by emu
可是你说的是泛形带来的其他问题,而这个问题其实不是泛形带来的。

# re: 关于Generics的一点儿理解  回复  更多评论   

2005-07-07 16:38 by david
那可能是我的表达不太清楚。不过我的本意是说Migration的问题。谢谢你评论。

# re: 关于Generics的一点儿理解  回复  更多评论   

2005-07-08 12:05 by 文章千古事,得失寸心知
>> 我想这是因为timestamp继承了Date的CompareTo()方法,所以一个timestamp就可以与Date比较了,但是应用了Generics以后这种情况是不允许的,而且代码也没有特殊处理,因此就会有Exception了。看来Generics也会带来一些其他的问题。

只有注册用户登录后才能发表评论。


网站导航: