Статьи

Календарь JDK 8. Строитель

Одной из определяющих характеристик дивного нового мира Java является растущая распространенность шаблона компоновщика в пространстве Java . Groovy , который представляется наиболее популярным альтернативным языком (Java) в JVM, хорошо известен своим интенсивным использованием Builder как в основных библиотеках, так и в библиотеках и инфраструктурах, поддерживаемых Groovy. Джош Блох (Josh Bloch) выдвинул шаблон на передний план мышления сообщества разработчиков Java, рассмотрев шаблон в пункте № 2 второго издания своей влиятельной книги « Эффективная Java» . В JDK было добавлено несколько сборщиков, включая Locale.Builder в J2SE 1.7 . В этом посте я кратко представляю Calendar.Builder, приходящий к JDK 8 .

Сегодня разработчик Java обычно заполняет экземпляр класса Calendar либо

вызывая один из методов «set», который принимает длинный список содержимого для экземпляра, или вызывая отдельные методы «set» в экземпляре один за другим. Эти два типичных подхода к Calendar экземпляра Calendar демонстрируются в следующих двух листингах кода.

Заполнение календаря одним методом set

01
02
03
04
05
06
07
08
09
10
11
12
/**
 * Demonstrate pre-JDK 8 method of instantiating Calendar instance using
 * "set" method for main fields.
 */
public static void demoCalendarWithSingleSet()
{
   final Calendar calendar =
      Calendar.getInstance(TimeZone.getTimeZone(timeZoneId),
         ENGLISH);
   calendar.set(2013, APRIL, 6, 15, 45, 22);
   out.println("Calendar via Constructor: " + stringifyCalendar(calendar));
}

Заполнение календаря несколькими отдельными «установленными» методами

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
/**
 * Demonstrate pre-JDK 8 method of instantiating Calendar instance using
 * individual "set" calls for each pair of field names and values.
 */
public static void demoCalendarWithIndividualSets()
{
   final Calendar calendar =
      Calendar.getInstance(
         TimeZone.getTimeZone(timeZoneId),
         ENGLISH);
   calendar.set(YEAR, 2013);
   calendar.set(MONTH, APRIL);
   calendar.set(DATE, 6);
   calendar.set(HOUR, 3);
   calendar.set(MINUTE, 45);
   calendar.set(SECOND, 22);
   calendar.set(AM_PM, PM);
   out.println("Calendar via set methods: " + stringifyCalendar(calendar));
}

ПОБОЧНОЕ ПРИМЕЧАНИЕ: в обоих приведенных выше примерах я использовал еще одну популярную особенность современной Java: статический импорт . Такие константы, как ENGLISH , YEAR и SECOND , фактически статически импортируются из таких классов, как Locale и Calendar . Как я уже писал ранее , кажется, что статический импорт становится все более популярным среди разработчиков на Java, особенно в свете тенденции к свободным интерфейсам.

Два «традиционных» подхода, показанных выше, показывают разные способы заполнения экземпляра Calendar . Одним из крайних случаев является установка каждого отдельного поля отдельно, а другим — установка всех значимых полей с помощью одного метода «set». У каждого подхода есть свои преимущества. В одном методе «set» меньше состояний «незавершенного» объекта, чем в подходе с множеством множеств, но подход с множеством множеств удобнее для чтения, поскольку имя устанавливаемого значения ясно на основе первого параметра каждого «множества» Метод. Подход с одним множеством немного громоздкий, потому что он требует шести целых чисел, которые можно легко перепутать в порядке передачи, потому что нет никакого очевидного способа определить, какое целое является каким, а не неявным порядком.

Calendar.Builder использует рекламируемые преимущества Builder, как описано в Bloch: устраняет существование «несовместимых состояний в процессе построения [объекта]». Это продемонстрировано в следующем листинге кода.

Calendar.Builder позволяет создавать отдельные инструкции с читаемыми настройками

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
    * Demonstrate using JDK 8's Calendar.Builder to instantiate an instance of
    * Calendar using the set methods to set each field individually based on
    * field name and value.
    */
   public static void demoCalendarWithCalendarBuilderSetFields()
   {
      final Calendar calendar =
         new Calendar.Builder()
            .set(YEAR, 2013)
            .set(MONTH, APRIL)
            .set(DATE, 6)
            .set(HOUR, 15)
            .set(MINUTE, 45)
            .set(SECOND, 22)
            .setTimeZone(TimeZone.getTimeZone(timeZoneId))
            .setLocale(ENGLISH)
            .build();
      out.println(
           "Calendar via Calendar.Builder 'set' Fields: "
         + stringifyCalendar(calendar));
   }

В приведенном выше листинге кода экземпляр Calendar создается И заполняется в одном операторе, что устраняет необходимость подвергать объект риску несовместимости в нескольких операторах. В этом примере сохраняется удобочитаемость традиционного индивидуального подхода «set» [ set (int, int) ] с дополнительной безопасностью полного заполнения объекта сразу при создании экземпляра.

Для разработчиков, которые хотят предоставить меньше отдельных методов «set», еще одна возможность с Calendar.Builder — использовать методы setDate (int, int, int) и setTimeOfDay (int, int, int), как показано в следующем листинге кода.

Calendar.Builder Установка даты и времени как два вызова

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
 * Demonstrate using JDK 8's Calendar.Builder to instantiate an instance of
 * Calendar using the "setDate" and "setTimeOfDay" builder methods.
 */
public static void demoCalendarWithCalendarBuilderSetDaySetTime()
{
   final Calendar calendar =
      new Calendar.Builder()
         .setDate(2013, APRIL, 6)
         .setTimeOfDay(15, 45, 22)
         .setTimeZone(TimeZone.getTimeZone(timeZoneId))
         .setLocale(ENGLISH).build();
   out.println(
        "Calendar via Calendar.Builder setDate/setTimeOfDay: "
      + stringifyCalendar(calendar));
}

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

Для разработчиков, которым нужна максимальная гибкость в определении параметров Calendar во время его создания, Calendar.Builder предоставляет метод setFields (int…), который принимает произвольную длину пары целых чисел, причем первое целое число пары представляет поле, которое нужно установить, и второе целое число пары, представляющее значение для этого поля. Этот метод используется в следующем листинге кода.

Задание полей календаря с помощью метода setFields Calendar.Builder

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
/**
    * Demonstrate using JDK 8's Calendar.Builder to instantiate an instance of
    * Calendar using the setFields method that allows providing of Calendar
    * fields as key/value pairs.
    */
   public static void demoCalendarWithCalendarBuilderSetPairs()
   {
      final Calendar calendar =
         new Calendar.Builder()
            .setFields(YEAR, 2013, MONTH, APRIL, DATE, 6, HOUR, 15, MINUTE, 45, SECOND, 22)
            .setTimeZone(TimeZone.getTimeZone("timeZoneId"))
            .setLocale(ENGLISH)
            .build();
      out.println(
           "Calendar via Calendar.Builder setPairs: "
         + stringifyCalendar(calendar));
   }

Этот setFields(int ...) повышает риск искажения порядка целых чисел, используемых для создания экземпляра нового экземпляра Calendar , но использование статически импортированных констант Calendar улучшает читабельность и снижает вероятность неправильного смешивания целых чисел. Если указано нечетное число целых чисел (что означает наличие неполной пары), генерируется исключение IllegalArgumentException .

Хотя Calendar.Builder обеспечивает некоторое удобство при создании и заполнении экземпляра Calendar , любой, кому посчастливится принять JDK 8, получит доступ к новому API даты / времени, поэтому можно задать вопрос: «Зачем использовать Calendar.Builder?» Возможно, лучший ответ заключается в том, что существуют миллионы строк существующего кода, библиотек и структур, использующих и ожидающих экземпляры Calendar , поэтому, вероятно, пройдет много времени, прежде чем потребность в Calendar полностью исчезнет (если вообще когда-либо). К счастью, Calandar.Builder позволяет легко преобразовать экземпляр Instant (часть нового API данных / времени Java ) в Calendar помощью CalendarBulder.setInstant (long) . Это продемонстрировано в следующем листинге кода.

Преобразование мгновенного в календарь с Calendar.Builder

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
    * Demonstrate using JDK 8's Calendar.Builder to instantiate an instance of
    * Calendar based on "now" Instant.
    */
   public static void demoCalendarWithCalendarBuilderInstant()
   {
      final Calendar calendar =
         new Calendar.Builder().setInstant(Instant.now().toEpochMilli())
            .setTimeZone(TimeZone.getTimeZone(timeZoneId))
            .setLocale(ENGLISH)
            .build();
      out.println("Calendar via Calendar.Builder and Instant: " + stringifyCalendar(calendar));
   }

Обратите внимание, что перегруженная версия метода setInstant принимает Date для создания экземпляра Calendar . В обоих случаях, независимо от того, setInstant(long) ли он с помощью setInstant(long) или setInstant(Date) , никакой другой метод «set» в Calender.Builder должен вызываться, чтобы избежать исключения IllegalStateException .

Легко пойти в другом направлении (получить Instant из Calendar ), используя Calendar.toInstant () . Другие методы, введенные в Calendar с помощью JDK 1.8, связаны с предоставлением типа календаря текущего экземпляра (в виде строки) или набора доступных типов календаря (набора строк). Когда я запускаю Calendar.getAvailableCalendarTypes () в моей системе, я вижу эти три строки: « gregory », « japanese » и « buddhist » (те же три календаря, задокументированные в поддерживаемых календарях )

Вывод

Как и многие разработчики Java , я с нетерпением жду улучшенного API данных / времени Java, встроенного в стандартный Java Development Kit. Тем не менее, я также понимаю, что, особенно в больших базах кода и при использовании библиотек и сред, ожидающих Calendar или Date , я не буду свободен от Calendar и Date течение некоторого времени. Внедрение Calendar.Builder в JDK 8 немного облегчает эту нагрузку.

Ссылка: календарь JDK 8 от нашего партнера JCG Дастина Маркса в блоге Inspired by Actual Events .