日历的防御性副本

时间:2012-02-10 08:36:54

标签: java calendar defensive-copy

一直试图找到实现制作Calendar对象防御性副本的方法的最佳方法。

例如:

public void setDate(Calendar date) {
    // What to do.... 
}

我特别担心在检查空输入和制作副本时是否存在线程交错,或者我是否遗漏了一些非常明显的东西?

7 个答案:

答案 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异常时,你是在防御性地编程吗?

Jon Skeet也提供了一个很好的答案,比我的好,但我认为这个问题的潜在绊脚石可能会受益于听到一点点“按合同设计”。虽然这种方法已经接近死亡,但这种方法已经帮助我的设计安静了许多。

答案 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”。