Оборонительная копия Календаря

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

например:

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

Я особенно беспокоюсь о чередовании потоков при проверке ввода с нулевым значением и создании копии или мне не хватает чего-то очень очевидного?

Ответы

Ответ 1

(На данный момент, похоже на немного другую аудиторию, я думаю...)

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

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

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

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

EDIT: с точки зрения "чередования потоков", о котором беспокоит исходный вопрос: значение параметра date не изменит никаких других потоков. Однако, если другой поток мутирует содержимое объекта, когда вы берете защитную копию, это может очень легко вызвать проблемы. Если это риск, тогда у вас больше проблем.

Ответ 2

Самый простой способ:

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

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

Ответ 3

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

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

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

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

Ответ 4

Просто обведите свой объект 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
    }
}

Ответ 5

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

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

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

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

Ответ 6

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

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

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

Ответ 7

Я предлагаю использовать "синхронизированный блок" здесь.