Защитная копия Календаря - PullRequest
17 голосов
/ 10 февраля 2012

Я пытался найти лучший способ реализовать метод, который делает защитную копию объекта Calendar.

Например:

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

Меня особенно беспокоит чередование потоковпроверка на нулевой ввод и создание копии или я что-то упускаю очень очевидное?

Ответы [ 7 ]

26 голосов
/ 29 января 2013

(нацелен на немного другую аудиторию, я думаю ...)

Я бы использовал clone(), если бы мне вообще пришлось использовать Calendar (вместо Joda Time). В комментариях вы утверждаете, что беспокоитесь о «непослушном подклассе» - как бы вы предложили обойти это в схеме любая ? Если вы ничего не знаете о задействованных подклассах и не доверяете им, то у вас нет способа сохранить специфичные для типа данные. Если вы не доверяете подклассу, чтобы не испортить вещи, у вас есть большие проблемы в целом. Как вы доверяете ему, чтобы дать вам правильные результаты при выполнении расчетов даты / времени?

clone() - это ожидаемый способ клонирования объектов: именно здесь я ожидал бы, что разумный подкласс зацепит любое поведение, зависящее от типа, в котором оно нуждается. Вам не нужно знать, какие биты состояния имеют отношение - вы просто позволяете типу разобраться с этим самим.

Преимущества по сравнению с Calendar.getInstance() и настройкой свойств самостоятельно:

  • Вы сохраните тот же тип календаря
  • Вам не нужно беспокоиться о том, чтобы забыть свойства: это ответственность типа
  • Вы явно говорите , что вы хотите сделать, и позволяете реализации позаботиться о how , что всегда приятно. Ваш код точно выражает ваше намерение.

РЕДАКТИРОВАТЬ: С точки зрения «чередования потоков», о котором беспокоится исходный вопрос: значение date параметра не изменит независимо от того, что делают другие потоки. Однако, если другой поток изменяет содержимое объекта, пока вы берете защитную копию, это может очень легко вызвать проблемы. Если это риск, то у вас больше проблем, в основном.

15 голосов
/ 10 февраля 2012

Простейшим способом будет:

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

Но я настоятельно рекомендую (по возможности) использовать JodaTime для выражения времени и даты в Java.У него есть как неизменяемые классы, так и изменяемые.

2 голосов
/ 27 сентября 2013

Я знаю, что это старый, но я подумал, что я бы положил свои два цента.

Если вы программируете по контракту, объект не несет ответственности за ошибки другого объекта.Календарь реализует Cloneable, что означает, что подклассы тоже!Если подкласс Calendar нарушает контракт Cloneable, то должен быть исправлен подкласс, а не класс, вызывающий клон.

В ОО-программировании объект должен заботиться только о классах и контрактах, с которыми он связан.Это существенно усложняет дизайн, когда вы спрашиваете: «Что, если подкласс нарушит его?»Всякий раз, когда объект принимает объект в качестве параметра, всегда существует вероятность того, что объект является подклассом и нарушает все.Вы программируете с защитой, когда вызываете getX (), чтобы он не генерировал исключение ArithmeticException для подклассов?

Джон Скит также дал отличный ответ, лучше моего, но я подумал, что потенциальный споткнуться на этот вопросможет выиграть от прослушивания незначительного кусочка «Дизайн по контракту».В то время как подход почти мертв, методология помогла моим проектам успокоиться.

1 голос
/ 04 февраля 2013

Просто оберните ваш объект Calendar в ThreadLocal.Это гарантирует, что каждый экземпляр Календаря используется только одним потоком.Как то так:

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
    }
}
0 голосов
/ 21 июля 2015

А как насчет ниже?

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

Правильное использование в коде синхронизированного зависит от вашего варианта использования.

0 голосов
/ 05 февраля 2013

Я бы предложил использовать здесь «синхронизированный блок».

0 голосов
/ 05 февраля 2013

Это невозможно гарантировать!

Безопасность потока: не может быть обеспечена, если вы не знаете о схеме безопасности, применяемой стороной, откуда вы получили ссылку. Эта сторона могла бы дать вам новую ссылку, и в этом случае вы можете просто использовать ссылку как есть. Эта сторона могла бы опубликовать свою схему безопасности в отношении этой ссылки в Календаре, и в этом случае вы можете следовать той же схеме (иногда это будет невозможно) для проверки ненулевых ссылок, напечатать и затем защитно скопировать с помощью getInstance () , Я думаю, что без их знания невозможно обеспечить безопасность потоков.

Защитное копирование календаря: клонирование не вариант, если вы не доверяете месту, откуда вы получили ссылку! Calendar не поддерживает конструктор, который принимает существующий объект Calender и создает новый!

Короче говоря, нет способа решить вашу проблему. JodaTime - лучший путь вперед.

...