Урок 11. Перечисления Java


В этой статье из моего курса уроков Java для начинающих я расскажу о перечислениях. Перечисления – это постоянные значения, которые никогда не могут быть изменены.

Ключевое слово final

Чтобы показать, почему это полезно, я начну с введения понятия постоянных значений в Java и расскажу об их недостатках. Предположим, мы хотели сохранить массив, как есть, чтобы в будущем, когда он будет использоваться, мы точно знали, какие ссылки находятся в каких местах. Мы могли бы сделать это с помощью модификатора переменной final. Переменная, объявленная как final, блокируется и ей не может быть присвоено новое значение. Например, если мы сделали persons2 final, а затем попытались установить его в null, Java выдаст исключение.

final Person[] persons2 = {new Person(), null, null};
persons2 = null; / / ошибка компиляции
Это может быть очень полезно, если у вас есть набор значений, которые вы хотите сохранить постоянными. Одна из вещей, которые вы можете сделать, - это создать набор значений состояния. Значение состояния, как следует из названия, говорят нам, в каком состоянии находится наша программа. Предположим, что наши три значения состояния для программы находятся в состоянии ожидания, обработки и обработки. Мы могли бы поместить эти строковые значения в массив и добавить последний тег, чтобы сохранить их. Мы также сделаем массив статическим, чтобы он был общим для всех объектов. Поскольку он не может быть изменен, нет причин не делиться им между объектами; кроме того, это помогает нам сообщить о нашем намерении, что эти значения состояния одинаковы для всех объектов. Мы будем называть этот массив MY_STATE_VALUES. Вот как это будет выглядеть:

private static final String MY_STATE_VALUES[] = {"PENDING", "PROCESSING", "PROCESSED"};
Вы можете заметить, что мое имя переменной не следует нотации camelCase по умолчанию,а вместо этого состоит из слов в верхнем регистре, соединенных подчеркиванием. Это потому, что наша "переменная" на самом деле не является переменной. Она статична и окончательна. Для статических конечных переменных всегда используйте заглавные буквы, соединенные символами подчеркивания.

Хотя мы не можем изменить нашу ссылочную переменную массива, мы можем перебирать ее, используя цикл for-each. Внутри этого цикла for-each мы могли бы сравнить наши состояния с определенным значением и вызвать некоторый метод, когда значение и состояние совпадают.

Сравнение строк

Поскольку String является объектом, а не примитивным значением, обычный оператор сравнения (‘==’) не работает. Вместо этого String использует метод equals() для определения равенства. Мы вызываем его с помощью Строка.equals(Другая строка). Возвращает он логическое значение (true / false) относительно того, равны ли две строки.

В цикле for-each я собираюсь создать три оператора if, по одному для каждого состояния в нашем массиве MY_STATE_VALUES. Обычно при сравнении строкового значения константы и переменной с помощью функции equals() записывается значение CONSTANT_VALUE.equals (переменная), потому что переменная может быть установлена в null, и это вызовет ошибку.

Если бы мы вместо этого написали "PENDING".equals(state) и состояние равно нулю, условие просто вернет false, и это не вызовет никакой ошибки. Это общий принцип, к которому я советую вам стремиться - всякий раз, когда вы можете, стремитесь писать стабильный код, даже когда вводимые данные недопустимы. В данном случае я решил этого не делать, потому что в данном конкретном случае переменная не может быть null, и мой код более удобочитаем таким образом.

for (String state : MY_STATE_VALUES) {

     if (state.equals("PENDING")) {
          // вызов метода
     }
     if (state.equals("PROCESSING")) {
          // вызов метода
     }
     if (state.equals("PROCESSED")) {
          // вызов метода
     }
}
Так что, похоже, это сработает отлично. Мы перебираем все в массиве MY_STATE_VALUES, и все три метода вызываются в правильном порядке. К сожалению, это не тот случай. Хотя модификатор "final" защищает нашу переменную ссылки на массив, он не защищает объект массива или любые его внутренние ссылки вообще. Если бы состояние было изменено на ”бла " прямо перед нашими заявлениями if, это было бы огромной проблемой. Но не волнуйтесь, есть решение: перечисление.

Перечисление

В то время как массив, помеченный final, предоставляет нам неизменяемый массив, как я показал выше, эти значения могут быть изменены, что является проблемой. Перечисление позволяет нам определить перечисление, которое означает полный упорядоченный список коллекции. Это перечисление не может быть изменено после создания. Примером перечисления в реальной жизни может служить светофор. Он имеет три различных значения: зеленый, желтый и красный; независимо от того, что происходит, вы никогда не сможете изменить эти значения. Это может быть чрезвычайно полезно, если применить к чему-то вроде набора состояний, о которых мы говорили в примерах 2 и 3. Давайте создадим перечисление LoggingLevel, которое хранит наши три состояния:

public enum LoggingLevel {
     PENDING, PROCESSING, PROCESSED;
}
Перечисление объявляется аналогично классу, используя "public enum EnumName". Я собираюсь вызвать наше перечисление LoggingLevel и для этого примера я буду использовать те же три состояния, которые мы использовали в нашем массиве: ожидание, обработка и обработано. Внутри декларации мы помещаем эти значения. Вам не нужны двойные кавычки, потому что это не строки. Это наш собственный набор значений перечисления. И это все! Вы определили свое перечисление.

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

Использование перечислений

Перечисления считаются постоянными значениями. Можно создать ссылочные переменные, указывающие на перечисления, и распечатать их значения. Вы также можете проверить равенство перечислений, используя ‘==' вместо метода equals(). Это происходит потому, что все перечисления являются константами, и мы знаем, что ни одно значение перечисления никогда не копируется. Поэтому проверка на идентичность - это то же самое, что проверка на равенство..

LoggingLevel currentLoggingLevel = LoggingLevel.PROCESSING;
System.out.println(“current LoggingLevel is:” + currentLoggingLevel);
System.out.println(currentLoggingLevel == LoggingLevel.PROCESSING); // true
Вы также можете выполнить поиск по перечислению, чтобы найти конкретное значение перечисления с помощью строки. Например:

LoggingLevel currentLoggingLevel = LoggingLevel.valueOf(“PROCESSING”);
LoggingLevel illegalLoggingLevel = LoggingLevel.valueOf(“processing”) // ошибка компиляции
Обратите внимание, что функциональность, показанная выше, чувствительна к регистру, что означает, что вторая строка кода вызовет ошибку компилятора.

Добавление значений в перечисления

Другая особенность перечислений заключается в том, что мы можем добавлять к ним конструкторы. Мы можем назначить много различных типов значений для каждого значения в вашем перечислении. В этом примере присвоим каждому значению уровня LoggingLevel числовые значения, отличные от значений по умолчанию. Если мы создадим частную переменную i и установим ее в число, которое мы отправим нашему конструктору, мы можем назначить каждому уровню ведения журнала значение, которое можно использовать. Поскольку конструктор и значение должны быть видны только внутри класса, я собираюсь сделать их private.

Поскольку перечисление вызовет конструктор самостоятельно, вам не разрешается создавать значения перечисления. Вот почему мы делаем наш конструктор перечислений private.

public enum LoggingLevel {
PENDING(1), PROCESSING(2), PROCESSED(3);
private int i;

private LoggingLevel(int i) {
     this.i = i;
}
Перечисления применительно к нашему примеру

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

for (LoggingLevel state : LoggingLevel.values()) {
     if (state == LoggingLevel.PENDING) {
          // call a method
     }
     if (state == LoggingLevel.PROCESSING) {
          // call a method
     }
     if (state == LoggingLevel.PROCESSED) {
          // call a method
     }
}
Если бы вы сравнили наш код с кодом

for (String state : MY_STATE_VALUES) {

     if (state.equals("PENDING")) {
          //call a method
     }
     if (state.equals("PROCESSING")) {
          //call a method
     }
     if (state.equals("PROCESSED")) {
          //call a method
     }
}
то, вы бы заметили, что нам не нужно использовать метод equals(), потому что мы больше не сравниваем два разных строковых объекта, мы сравниваем значения констант enum. Это работает, потому что именно так Java определяет перечисления. За кулисами Java преобразует наши значения перечисления в конечные примитивные значения, которые неизменяемы. Поэтому мы знаем, что всякий раз, когда мы называем LoggingLevel.В ожидании, это всегда будет одно и то же значение.

На самом деле мы можем сократить наш код еще больше, если мы добавим что-то под названием switch-оператор, чтобы заменить оператор if, но мы сделаем это в следующей статье, которая подробно обсуждает оператор Java switch.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, уроки по java, java, перечисления




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




4 подсказки для победы в позиции с преимуществом
Базы данных ADO.NET на языке C++\CLI
Лабиринты Java, часть 2: класс Waypoint