一直在尝试找到最佳方法来实现一个可以对日历对象进行防御性复制的方法。
例如:
public void setDate(Calendar date) {
// What to do....
}
我尤其关注的是在线程检查空输入并进行复制时的交错问题,或者我是否忽略了什么非常明显的东西?
一直在尝试找到最佳方法来实现一个可以对日历对象进行防御性复制的方法。
例如:
public void setDate(Calendar date) {
// What to do....
}
我尤其关注的是在线程检查空输入并进行复制时的交错问题,或者我是否忽略了什么非常明显的东西?
现在的受众对象可能略有不同了...
如果我真的必须使用Calendar
(而不是Joda Time),我会使用clone()
。您在评论中争辩说您担心“淘气子类”-您如何提议在任何方案中解决这个问题? 如果您不知道涉及的子类,也不信任它们,那么您就无法保留特定于类型的数据。如果您不相信子类不会弄乱事情,那么您一般会面临更大的问题。当进行日期/时间计算时,您如何相信它将为您提供正确的结果?
clone()
是克隆对象的预期方法:我期望一个明智的子类在其中钩入任何需要的特定于类型的行为。您不需要知道哪些状态位是相关的-您只需让类型自己处理即可。
Calendar.getInstance()
并自己设置属性相比的优势:
编辑:关于原始问题担心的“线程交错”:无论其他线程做什么,date
参数的值都不会更改 。但是,如果另一个线程在您进行防御性复制时修改对象的内容,则可能非常容易导致问题。如果这是一个风险,那么您基本上面临着更大的问题。
最简单的方法是:
copy = Calendar.getInstance(original.getTimeZone());
copy.setTime(original.getTime());
我强烈建议(尽可能地)在Java中使用JodaTime来表示时间和日期。它有可变和不可变的类。
我知道这已经过时了,但我想发表一下我的看法。
如果您按照合同编写程序,则对象不负责另一个对象的错误。日历实现了Cloneable,这意味着子类也会实现!如果Calendar的子类违反了Cloneable合同,则需要纠正子类,而不是调用clone的类。
在面向对象编程中,一个对象只应关心它所涉及的类和合同。当您问“如果子类违反了它怎么办?”时,它会大大地复杂化设计,因数而异。每当对象将对象作为参数传递时,永远存在该对象是子类且会破坏所有内容的可能性。在调用getX()时,为了子类不抛出ArithmeticException异常,您是否进行防御性编程?
Jon Skeet也提供了一个非常好的答案,比我的更好,但我认为此问题的潜在触发者可能从听到“按合同设计”这个小小的点中受益匪浅。虽然方法已经接近死亡,但方法论对我的设计有很大的帮助。
public class ThreadLocalCalendar
{
private final static ThreadLocal <Calendar> CALENDAR =
new ThreadLocal <Calendar> ()
{
@Override
protected Calendar initialValue()
{
GregorianCalendar calendar = new GregorianCalendar();
// Configure calendar here. Set time zone etc.
return calendar;
}
};
// Called from multiple threads in parallel
public void foo ()
{
Calendar calendar = CALENDAR.get ();
calendar.setTime (new Date ());
// Use calendar here safely, because it belongs to current thread
}
}
public synchronized void setDate(Calendar date) {
// What to do....
Calendar anotherCalendar = Calendar.getInstance();
anotherCalendar.setTimeInMillis(date.getTimeInMillis());
}
在编程中,synchronized 的正确使用取决于您的用例。
这是不可能保证的!
线程安全:除非您了解从引用来源实施的安全方案,否则无法保证线程安全。该方可能会给您提供一个新的参考,这种情况下,您可以直接使用参考。该方可能已经针对该日历参考发布了他们的安全方案,在这种情况下,您可以遵循相同的方案(有时不可能)来检查非空引用、类型,然后通过使用getInstance()进行防御性复制。如果没有了解这些,我认为无法确保线程安全。
Calendar的防御性复制:如果您不信任引用来源的地方,则克隆不是一种选择!Calendar不支持任何使用现有Calendar对象创建新对象的构造函数!
简而言之,没有解决问题的方法。JodaTime是前进的最佳方式。