Defensive copy of Calendar

心已入冬 提交于 2019-11-30 00:52:21

问题


Been trying to find the best way to implement a method that makes a defensive copy of a Calendar object.

eg:

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

I am particularly concerned about interleaving of threads when checking for null input and making the copy or am I missing something very apparent?


回答1:


(Aimed at a slightly different audience now, I guess...)

I would use clone() if I absolutely had to use Calendar at all (instead of Joda Time). You argue in comments that you're worried about a "naughty subclass" - how would you propose to work around that in any scheme? If you don't know anything about the subclasses involved, and don't trust them, then you've got no way of preserving type-specific data. If you don't trust the subclass not to mess things up, you've got bigger problems in general. How do you trust it to give you the right results when performing date/time calculations?

clone() is the expected way of cloning objects: it's where I'd expect a sensible subclass to hook in any type-specific behaviour it needed. You don't need to know which bits of state are relevant - you just let the type deal with that itself.

Benefits over using Calendar.getInstance() and setting properties yourself:

  • You'll preserve the same calendar type
  • You don't need to worry about forgetting properties: that's the responsibility of the type
  • You're explicitly saying what you want to do, and letting the implementation take care of the how, which is always nice. Your code expresses your intention exactly.

EDIT: In terms of the "thread interleaving" which the original question worries about: the value of the date parameter will not change whatever other threads do. However, if another thread is mutating the contents of the object while you take a defensive copy, that could very easily cause problems. If that's a risk, then you've got bigger issues, basically.




回答2:


Simplest way would be:

copy = Calendar.getInstance(original.getTimeZone());
copy.setTime(original.getTime());

But I strongly suggest that (whenever possible) you use JodaTime to express times and dates in Java. It has immutable classes as well as mutable.




回答3:


I know this is old but I thought I'd put in my two cents.

If you program by contract, an object isn't responsible for another object's mistakes. Calendar implements Cloneable, which means the subclasses do too! If a subclass of Calendar breaks the Cloneable contract, it is the subclass that needs to be corrected, not the class calling clone.

In OO programming, an object should only care about classes & contracts it is involved with. It complicates the design greatly, by factors, when you ask "what if a subclass breaks it?" Whenever an object takes an object as a parameter, there is always the chance the object is a subclass and breaks everything. Do you program defensively when you call getX() that it doesn't throw a ArithmeticException exception for subclasses?

Jon Skeet provided a great answer too, better than mine, but I thought that a potential stumbler onto this question may benefit from hearing a minor bit of 'Design By Contract'. While the approach is near dead, the methodology has helped my designs quiet a lot.




回答4:


Just wrap your Calendar object into ThreadLocal. This will guarantee that each instance of Calendar is used only by one thread. Something like this:

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
    }
}



回答5:


This is impossible to guarantee!

Thread Safety: Cannot be ensured unless you know about the safety scheme implemented by the party from where you got the reference. That party could have given you a new reference in which case you can simply use the reference as is. That party could have published their safety scheme with respect to that Calendar reference, in which case you can follow the same scheme (won't be possible sometimes) to check for non-null references, type and then copy defensively by using getInstance(). Without knowing these, it is impossible to ensure thread safety, I think.

Defensive Copying of Calendar: cloning isn't an option if you don't trust the place from where you got the reference from! Calendar does not support any constructor that takes an existing Calender object and creates a new one!

In short, there is no way to solve your issue. JodaTime is the best way forward.




回答6:


What about the below ?

public synchronized void setDate(Calendar date) {
    // What to do.... 
    Calendar anotherCalendar = Calendar.getInstance();
    anotherCalendar.setTimeInMillis(date.getTimeInMillis());
}

The correct usage in code of synchronized depends on your use case.




回答7:


I'd suggest the use of "synchronized block" here.



来源:https://stackoverflow.com/questions/9224935/defensive-copy-of-calendar

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!