一直试图找到实现制作Calendar对象防御性副本的方法的最佳方法。
例如:
public void setDate(Calendar date) {
// What to do....
}
我特别担心在检查空输入和制作副本时是否存在线程交错,或者我是否遗漏了一些非常明显的东西?
答案 0 :(得分:25)
(针对现在略有不同的观众,我猜......)
如果我绝对不得不使用clone()
(而不是Joda Time),我会使用Calendar
。你在评论中争论说你担心“顽皮的子类” - 你会如何建议在任何计划中解决这个问题?如果您对所涉及的子类一无所知,并且不信任它们,那么您无法保留特定于类型的数据。如果你不相信子类不要搞砸了,你通常会遇到更大的问题。在执行日期/时间计算时,您如何相信它能为您提供正确的结果?
clone()
是克隆对象的预期方式:它是我希望敏感子类挂钩它所需的任何特定于类型的行为的地方。您不需要知道哪些状态位是相关的 - 您只需让类型处理它本身。
使用Calendar.getInstance()
并自行设置属性的好处:
编辑:就原始问题所担心的“线程交错”而言:date
参数的值不会改变其他线程所做的事情。但是,如果另一个线程在您采取防御性副本时正在改变对象的内容,那么可能很容易导致问题。如果这是一个风险,那么基本上你就会遇到更大的问题。
答案 1 :(得分:15)
最简单的方法是:
copy = Calendar.getInstance(original.getTimeZone());
copy.setTime(original.getTime());
但我强烈建议(尽可能)使用JodaTime来表达Java中的时间和日期。它具有不可变类和可变类。
答案 2 :(得分:2)
我知道这已经过时了,但我想我已经投入了两美分。
如果按合同编程,则对象不对另一个对象的错误负责。 Calendar实现了Cloneable,这意味着子类也可以!如果Calendar的子类违反了Cloneable合约,那么它是需要更正的子类,而不是类调用clone。
在面向对象编程中,一个对象应该只关心类&它涉及的合同。当你问“如果一个子类破坏了它会怎么样?”时,它会因为因素而极大地复杂化设计。每当一个对象将一个对象作为一个参数时,该对象总是有可能成为一个子类并且会破坏所有东西。当你调用getX()它没有为子类抛出ArithmeticException异常时,你是在防御性地编程吗?
答案 3 :(得分:1)
将Calendar对象包装到ThreadLocal中。这将保证每个Calendar实例仅由一个线程使用。像这样:
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
}
}
答案 4 :(得分:0)
这是不可能保证的!
线程安全:除非您了解由您获得参考的一方实施的安全方案,否则无法确保。该方可以给你一个新的参考,在这种情况下你可以简单地使用参考。该方可以针对该Calendar引用发布其安全方案,在这种情况下,您可以遵循相同的方案(有时不可能)来检查非空引用,键入然后使用getInstance()进行防御性复制。不知道这些,我认为不可能确保线程安全。
日历的防御性复制:如果您不信任您从中获取引用的地方,则无法进行克隆! Calendar不支持任何带有现有Calender对象并创建新对象的构造函数!
简而言之,没有办法解决您的问题。 JodaTime是最好的前进方式。
答案 5 :(得分:0)
下面怎么样?
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
[songInfo setObject:someTitle forKey:MPMediaItemPropertyTitle];
[songInfo setObject:someArtist forKey:MPMediaItemPropertyArtist];
[songInfo setObject:someAlbum forKey:MPMediaItemPropertyAlbumTitle];
MPMediaItemArtwork *albumArt;
if (song.artwork){
albumArt = [[MPMediaItemArtwork alloc] initWithImage: someArtwork];
}
else {
albumArt = [[MPMediaItemArtwork alloc] init]; // make sure to remove the artwork if none is found for the current track
}
[songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
同步代码中的正确用法取决于您的用例。
答案 6 :(得分:-2)
我建议在这里使用“synchronized block”。