为什么Java日期API(java.util.Date,.Calendar)如此混乱?

68

现在大多数人都痛苦地意识到,处理日历日期的Java API(特别是类java.util.Datejava.util.Calendar)非常混乱。

就我所知:

  • Date是可变的
  • Date表示时间戳,而不是日期
  • 没有简单的方法将日期组件(日、月、年等)与Date相互转换
  • Calendar使用起来很笨拙,并试图将不同的日历系统合并到一个类中

这篇文章(链接失效 - 存档链接)很好地总结了这些问题,JSR-310也解释了这些问题。

现在我的问题是:

这些类是如何进入Java SDK的?大多数问题似乎相当明显(特别是Date可变),应该很容易避免。那么它是怎么发生的?时间压力?还是问题只有在回顾时才显而易见?
我意识到这不是严格的编程问题,但我认为了解API设计如何出错很有趣。毕竟,错误总是一个很好的学习机会(而且我很好奇)。

2
希望基于Joda-Time的JSR-310的新日期和时间API能够在Java 7中发布。 - Pascal Thivent
5
日期格式化类 DateFormat 并非设计为线程安全的... (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4093418) - Arjan
10
@S.Lott:我不同意。我们生活在一个非孤立的世界中。我们并非天生就是具备无需预见即知道正确解决方案的伟大软件架构师。我们必须学习,并从过去的错误中吸取经验。 - Mario Ortegón
1
二月是第一个月。 - Thilo
2
@Thilo:没错,但这实际上是它问题中最不重要的。我 可能 会原谅这个问题,因为程序员习惯从0开始计数。但另一方面,MONTH_OF_DAY和DAY_OF_YEAR是以1为基础的...所以这真的是无法原谅的。 - sleske
显示剩余2条评论
3个回答

45

有人比我说得更好:

  • Date类表示具有毫秒精度的特定时间点。这个类的设计是一个非常糟糕的玩笑 - 它让人清醒地看到了即使优秀的程序员也会出错的例子。现在,大多数Date中的方法都已被弃用,由下面的类中的方法替代。
  • Calendar类是一个抽象类,用于在Date对象与一组整数字段(如年、月、日和小时)之间进行转换。

  • GregorianCalendar类是JDK中唯一的Calendar子类。它执行常用日历系统的日期到字段的转换。Sun从Taligent获得了这个过度工程化的垃圾 - 这是一个令人警醒的例子,说明普通程序员也会犯错。

来自Java程序员常见问题解答,版本为07.X.1998,作者Peter van der Linden - 后来的版本中删除了这部分内容。

至于可变性,很多早期的JDK类都受到了它的影响(PointRectangleDimension等)。有些人说这是错误的优化。

想法是你希望能够重用对象(o.getPosition().x += 5),而不是像不可变对象一样创建副本(o.setPosition(o.getPosition().add(5, 0)))。在早期的VM中,这甚至可能是个好主意,但在现代VM中则可能不是。


1
几何类是可变的,有些具有公共字段,因为Swing创建了许多实例,在最初这是一种提高性能的hack。 - mP.
10
哈,我来这里就是要发表这个!不能编辑你的帖子(需要更多声望),但:“... Date 表示时间中的一个特定时刻,精确到毫秒。这个类的设计非常糟糕——这是一个令人深思的例子,说明即使是好的程序员也会搞砸[...] GregorianCalendar 是 JDK 中唯一的 Calendar 子类。[...] Sun 从 Taligent 获得了这个过度工程化的垃圾,这是一个让普通程序员搞砸的令人深思的例子。”来源:Peter van Der Linden 的 comp.lang.java.programmers "Java 程序员 FAQ",在线网址:例如 http://www.faqs.org/faqs/computer-lang/java/programmers/faq/ - Cowan
@Cowan:你确定吗?我在你提供的常见问题解答中没有找到这个引用。 - sleske
6
有趣的是,FAQ 似乎曾经更改过以删除引用。请比较先前版本(原来位于该链接)http://www.cs.colorado.edu/~karl/APlus/javafaq.html (参见第5.8节),其中包含上述引用,与我提供的版本进行比较——该版本还说“感谢 Paul Hill 彻底改写了本节,并感谢 IBM 的程序员们实现了大量的Java日期代码,并审核了此FAQ部分以确保准确性。”对我来说,这听起来非常可疑,就好像有人不喜欢这种描述一样 :) - Cowan

21

Java早期的API只是当时技术水平的产物。在那之后才流行"不可变性"的概念。你说现在"不可变性"很"显然",但在过去却不是这样。就像依赖注入现在很"显然",但在10年前并不是。

创建Calendar对象也曾经是一件代价高昂的事情。

为了保持向后兼容性,它们仍然是代价高昂的。更不幸的是,一旦意识到错误,旧类并没有被弃用,而是新的日期/时间类被创建用于所有后续的API。虽然在JDK 8中采用了像JodaTime API(java.time,JSR 310)一样的API,但实际上已经太迟了。


1
日期比日历早发布了几个版本。日期从一开始就存在,而且不容易被删除。添加了日历是因为日期不完整。 - S.Lott
我非常清楚地记得,String的不可变性曾经是一种新颖之处,既受到了批评,也受到了赞扬。 - BOC

9
时间本身并不容易测量和处理。只需看看维基百科关于时间的文章长度。此外,人们对时间本身有不同的理解:绝对时间点(作为常数),某个地方的时间点,时间范围,时间分辨率等。
我记得当我第一次看到java.util.Date时(JDK 1.0?),我真的很高兴。我所知道的语言没有这样的功能。我不必考虑时间转换等问题。
我认为这很混乱,因为一切变化都会留下混乱,如果你从一个理解水平(XMLGregorianCaldender vs. Date)和要求(纳秒,2030年后)发展到更高的水平,但保持旧的不变。而java.util.Date并不是一个例外。只需看看I/O子系统或从AWT到Swing的转换...
正因为如此,“我们有时应该按下重置按钮。”(顺便问一下,谁说的?)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接