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) {
I'd suggest the use of "synchronized block" here.
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.
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.
(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:
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.
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.
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
}
}