为什么在使用传统API时,Date比Calendar更适合属性类型



首先,请注意,这个问题不是这个问题的重复:Java日期与日历。我的问题要具体得多。引用的问题问"什么"(或"哪个"),但我已经知道"什么",正在问"为什么"。

我所在的团队正在为一个客户机对现有Java项目进行增强。这个Java项目使用java6,并且没有JodaTime作为依赖项。经过询问,似乎添加Joda Time或升级到Java 8都不是选项。

因此,当涉及到将日期/时间表示为对象中的字段时,我们必须使用Calendar或date进行属性键入。这个项目的遗留代码中充斥着使用日历来表示日期/时间字段的对象,这些字段我们永远不会有理由进行操作(如添加或减去时间单位等)。我知道这是一种糟糕的做法,因为日历是一个更复杂的对象,而日期更简单,也同样有效。(当然,我知道这两者基本上都是长时期millis的包装器,是可变的,设计也很糟糕,但这也是我们仅有的两种选择。)

换句话说,像这样的对象:

public class Reservation {
    private Guest guest;
    // Set only once, never used for calculations
    private Calendar dateReserved;
    ...
}

应该是这样的:

public class Reservation {
    private Guest guest;
    // Set only once, never used for calculations
    private Date dateReserved;
    ...
}

然后我注意到,当为新功能添加新对象时,我的团队遵循了使用日历而不是日期的相同约定。当我提到这一点时,我的回答是最好使用Calendar,因为它可以做更多的事情,而且不像Date那样有所有这些不推荐使用的方法。

我知道这种推理过于简单化了。我还看到对更广泛的用法问题的回答表达了相同的观点,即日历不应用于属性类型。然而,答案并没有包含太多关于为什么不应该首选日历的解释。

所以我已经知道"什么"了。但我正在努力向我的团队证明这一点,所以我的问题是,"为什么"?为什么在键入属性时,Date应该优先于Calendar?使用"日历"而不是"日期"进行属性键入的缺点是什么?

我同意Jon Skeet关于日历系统和时区的评论,我认为你的前提存在根本缺陷。日期并不比日历好。如果你从来没有比较过时间,或者从来没有在不同的时区有过两个日期,那么当然,我想,更小的足迹可能很好,但在这一点上,只需要使用long和Unix时间戳。日历是迄今为止更好的对象模型,毕竟,如果你绝对需要它,你可以从中获得Date对象

如果您在键入属性时不得不在Date和Calendar之间进行选择:如果以下任一项为真,请使用日历:

  • 您需要能够在最初设置日期/时间后对其进行调整(例如更改月份,同时保持日期和时间不变)。

  • 你需要注意时区。

否则,由于以下原因使用日期:

  1. 准确地表达你的意图。如果你使用日历,你就意味着你想要某种你实际上并不打算使用的功能(时区、更改日期或月份等)
  2. 减少了字符串表示的麻烦。例如,考虑这个类:

    public class Reservation {
        private Guest guest;
        private Calendar dateReserved;
        @Override
        public String toString() {
            return String.format("Reservation{guest=%s,dateReserved="%s"}",
                    guest, dateReserved);
        }
    }
    

    现在,如果你打印出这个类的一个实例,你会得到一些可怕的东西:

    Reservation{guest=Guest{id=17,name="John Smith"},dateReserved="java.util.GregorianCalendar[time=1426707020619,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=2,WEEK_OF_YEAR=12,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=77,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=0,HOUR_OF_DAY=12,MINUTE=30,SECOND=20,MILLISECOND=619,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]"}

    然而,如果你使用Date,你会得到这个:

    Reservation{guest=Guest{id=17,name="John Smith"},dateReserved="Wed Mar 18 12:34:26 PDT 2015"}

    因此,如果您使用Calendar并且希望toString()可用,则需要调用dateReserved.getTime(),这意味着您需要添加一个null检查。这适用于是否最终使用DateFormat对象。

  3. Date是一个较小的对象,实例化速度更快,开销更少。

  4. 日期实际上是不可变的——这意味着更改日期对象的唯一方法是使用不推荐使用的方法。因此,正如第1点所说,表达你的意图很重要。如果你的日期字段应该是不可变的,不要让将来会使用日历来接触你代码的开发人员感到困惑(当然,除非你需要时区意识)。

  5. 对于表示单个时间点的字段类型,"日期"是一个比"日历"更直观的名称。

Date对象比Calendar对象具有更少的字段,占用更少的内存,并且实例化速度更快。

最新更新