1. Обзор
Мы видим перечисления почти в каждом приложении. К ним относятся коды состояния заказа, такие как ЧЕРНОВИК
и ОБРАБОТКА,
а также коды веб-ошибок, такие как 400, 404, 500, 501 и т. д. Всякий раз, когда мы видим пронумерованные данные в домене, мы будем видеть Enum
для них в нашем приложении. Мы можем использовать данные во входящем запросе и найти это перечисление. Например, мы можем сопоставить веб-ошибку 400
с BAD_REQUEST
.
Таким образом, нам нужна логика для поиска перечисления по критериям. Это может быть его имя или значение. В качестве альтернативы, это может быть даже произвольный целочисленный код.
В этом уроке мы узнаем, как искать перечисление по критериям. Кроме того, мы также рассмотрим различные способы возврата найденного перечисления.
2. Поиск Enum по имени
Начнем с того, что мы знаем, что перечисляемый тип — это особый тип данных. Это позволяет переменной быть набором предопределенных констант. Давайте определим перечисление для направления:
public enum Direction {
EAST, WEST, SOUTH, NORTH;
}
Имя значения перечисления является константой. Так, например, имя Direction.EAST
— EAST
. Теперь мы можем искать направление по его названию. Рекомендуется реализовать поиск без учета регистра. В результате East
, east
и EAST
будут отображаться в Direction.EAST
. Добавим в перечисление Direction
следующий метод :
public static Direction findByName(String name) {
Direction result = null;
for (Direction direction : values()) {
if (direction.name().equalsIgnoreCase(name)) {
result = direction;
break;
}
}
return result;
}
В этой реализации мы возвращаем null
, если не находим перечисление для заданного имени. От нас зависит, как мы будем относиться к ненайденному сценарию. Один из вариантов заключается в том, что мы можем вернуть значение перечисления по умолчанию. И наоборот, мы можем сгенерировать исключение. Вскоре мы увидим больше примеров поиска в перечислении. Теперь давайте проверим нашу логику поиска. Во-первых, положительный сценарий:
@Test
public void givenWeekdays_whenValidDirectionNameProvided_directionIsFound() {
Direction result = Direction.findByName("EAST");
assertThat(result).isEqualTo(Direction.EAST);
}
В конце этой статьи мы предоставим ссылку на полную реализацию кода, но сейчас мы сосредоточимся на фрагментах кода. Здесь мы искали направление по имени «ВОСТОК» и ожидаем получить Direction.EAST
. Как упоминалось ранее, мы знаем, что поиск не чувствителен к регистру, поэтому мы должны получить тот же результат для имени «восток» или «восток». Давайте подтвердим наши ожидания:
@Test
public void givenWeekdays_whenValidDirectionNameLowerCaseProvided_directionIsFound() {
Direction result = Direction.findByName("east");
assertThat(result).isEqualTo(Direction.EAST);
}
Мы также могли бы добавить еще один тест, чтобы проверить, возвращает ли метод поиска тот же результат для имени «Восток». Следующий тест иллюстрирует, что мы получаем тот же результат для имени «Восток».
@Test public void givenWeekdays_whenValidDirectionNameLowerCaseProvided_directionIsFound() {
Direction result = Direction.findByName("East");
assertThat(result).isEqualTo(Direction.EAST);
}
3. Поиск Enum по значению
Теперь давайте определим перечисление для дней недели. На этот раз давайте укажем значение вместе с именем. Фактически, мы можем определить любой элемент данных внутри перечисления, а затем использовать его для логики нашего приложения. Вот код для перечисления дня недели
:
public Weekday {
MONDAY("Monday"),
TUESDAY("Tuesday"),
// ...
SUNDAY("Sunday"),
;
private final String value;
Weekday(String value) {
this.value = value;
}
}
Далее реализуем поиск по значению. Итак, для «понедельника» мы должны получить Weekday.MONDAY
. Добавим в перечисление следующий метод:
public static Weekday findByValue(String value) {
Weekday result = null;
for (Weekday day : values()) {
if (day.getValue().equalsIgnoreCase(value)) {
result = day;
break;
}
}
return result;
}
Здесь мы перебираем константы перечисления, а затем сравниваем введенное значение со значением члена перечисления. Как упоминалось ранее, мы игнорируем регистр значений. Теперь мы можем протестировать его:
@Test
public void givenWeekdays_whenValidWeekdayValueProvided_weekdayIsFound() {
Weekday result = Weekday.findByValue("Monday");
assertThat(result).isEqualTo(Weekday.MONDAY);
}
Если мы не предоставим допустимое значение, в ответ мы получим null .
Давайте подтвердим это:
@Test
public void givenWeekdays_whenInvalidWeekdayValueProvided_nullIsReturned() {
Weekday result = Weekday.findByValue("mon");
assertThat(result).isNull();
}
Поиск не всегда должен осуществляться по строковым значениям. Это было бы довольно неудобно, так как нам пришлось бы сначала преобразовать ввод в строку, а затем передать ее методу поиска. Теперь давайте посмотрим, как выполнять поиск по нестроковым значениям, таким как целочисленное значение.
4. Поиск Enum по целочисленному значению
Давайте определим новое перечисление под названием Month
. Вот код для перечисления месяца
:
public enum Month {
JANUARY("January", 1),
FEBRUARY("February", 2),
// ...
DECEMBER("December", 12),
;
private final String value;
private final int code;
Month(String value, int code) {
this.value = value;
this.code = code;
}
}
Мы видим, что перечисление месяца состоит из двух элементов: значения и кода, причем код представляет собой целочисленное значение. Давайте реализуем логику поиска месяцев по их коду:
public static Optional<Month> findByCode(int code) {
return Arrays.stream(values()).filter(month -> month.getCode() == code).findFirst();
}
Этот поиск немного отличается от предыдущих, потому что мы использовали возможности Java 8, чтобы продемонстрировать другой способ реализации поиска. Здесь вместо того, чтобы возвращать само перечисление, мы вернем необязательное
значение перечисления. Точно так же вместо null
мы вернем пустой option
. Итак, если мы ищем месяц для кода 1, мы должны получить Month.JANUARY
. Давайте проверим это с помощью теста:
@Test
public void givenMonths_whenValidMonthCodeProvided_optionalMonthIsReturned() {
Optional<Month> result = Month.findByCode(1);
assertThat(result).isEqualTo(Optional.of(Month.JANUARY));
}
Для недопустимых значений кода мы должны получить пустой необязательный
. Давайте также проверим это с помощью теста:
@Test
public void givenMonths_whenInvalidMonthCodeProvided_optionalEmptyIsReturned() {
Optional<Month> result = Month.findByCode(0);
assertThat(result).isEmpty();
}
Могут быть случаи, когда мы хотим реализовать более строгий поиск. Таким образом, мы не потерпим недопустимых входных данных и будем генерировать исключения, чтобы продемонстрировать это.
5. Исключения, генерируемые методами поиска
Вместо того, чтобы возвращать нулевое
или пустое дополнительное
значение, мы можем захотеть создать исключение. Какое исключение выбрасывать, полностью зависит от потребностей системы. Мы выберем исключение IllegalArgumentException
, если не найдем перечисление. Вот код метода поиска:
public static Month findByValue(String value) {
return Arrays.stream(values()).filter(month -> month.getValue().equalsIgnoreCase(value)).findFirst().orElseThrow(IllegalArgumentException::new);
}
Мы снова видим, что мы используем стиль Java 8 при генерации исключения. Давайте проверим это с помощью теста:
@Test
public void givenMonths_whenInvalidMonthValueProvided_illegalArgExIsThrown() {
assertThatIllegalArgumentException().isThrownBy(() -> Month.findByValue("Jan"));
}
Методы поиска, продемонстрированные в этой статье, не являются единственным способом сделать это, но они представляют собой наиболее распространенные варианты. Мы также можем настроить эти реализации в соответствии с потребностями нашей системы.
6. Заключение
В этой статье мы узнали о различных способах поиска в перечислениях. Мы также обсудили различные способы возврата результатов. Наконец, мы подкрепили эти реализации надежными модульными тестами.
Как всегда, код, относящийся к этой статье, доступен на GitHub .