Avatar ·

Как получить значение enum по строке в Java

Всем привет! Я новичок в Java и столкнулся с проблемой при работе с перечислениями (enum). У меня есть такой enum для статусов заказа: ```java public enum OrderStatus { NEW, PROCESSING, SHIPPED, DELIVERED, CANCELLED } ``` Я получаю из базы данных строку, например "PROCESSING", и мне нужно преобразовать её обратно в значение enum `OrderStatus.PROCESSING`. Я пробовал использовать простое приведение типов, но это не работает: ```java String statusStr = "PROCESSING"; OrderStatus status = (OrderStatus) statusStr; // Ошибка компиляции ``` Я также пытался использовать `valueOf()`, но когда строка не соответствует ни одному значению enum, выбрасывается исключение `IllegalArgumentException`. Например, если пользователь ввёл "processing" (с маленькой буквы) или допустил опечатку, программа падает. Подскажите, как правильно и безопасно конвертировать строку в enum? Нужно ли как-то обрабатывать регистр букв? И что делать, если строка вообще не соответствует ни одному значению? Заранее спасибо за помощь! <<>>

Основной способ: метод valueOf()

В Java у каждого enum есть встроенный статический метод valueOf(String), который преобразует строку в соответствующий элемент перечисления. Он чувствителен к регистру и выбрасывает IllegalArgumentException, если строка не найдена.

String statusStr = "PROCESSING";
OrderStatus status = OrderStatus.valueOf(statusStr);
System.out.println(status); // PROCESSING

Проблема с регистром

Если строка приходит в другом регистре (например, "processing" или "Processing"), нужно её нормализовать. Самый простой способ — привести к верхнему регистру:

String userInput = "processing";
OrderStatus status = OrderStatus.valueOf(userInput.toUpperCase());

Безопасное преобразование с обработкой ошибок

Чтобы избежать исключений при невалидных строках, используйте try-catch:

public static OrderStatus safeValueOf(String value) {
    try {
        return OrderStatus.valueOf(value.toUpperCase());
    } catch (IllegalArgumentException e) {
        System.out.println("Неизвестный статус: " + value);
        return OrderStatus.NEW; // значение по умолчанию
    }
}

Лучшая практика: кастомный метод с Map

Для сложных случаев (например, когда строки не совпадают с именами enum) создайте статический Map внутри enum:

import java.util.HashMap;
import java.util.Map;

public enum OrderStatus {
    NEW("новый"),
    PROCESSING("в обработке"),
    SHIPPED("отправлен"),
    DELIVERED("доставлен"),
    CANCELLED("отменён");

    private final String displayName;
    
    private static final Map<String, OrderStatus> BY_NAME = new HashMap<>();
    
    static {
        for (OrderStatus status : values()) {
            BY_NAME.put(status.displayName.toLowerCase(), status);
            BY_NAME.put(status.name().toLowerCase(), status);
        }
    }

    OrderStatus(String displayName) {
        this.displayName = displayName;
    }

    public String getDisplayName() {
        return displayName;
    }

    public static OrderStatus fromString(String value) {
        if (value == null || value.trim().isEmpty()) {
            throw new IllegalArgumentException("Статус не может быть пустым");
        }
        OrderStatus result = BY_NAME.get(value.trim().toLowerCase());
        if (result == null) {
            throw new IllegalArgumentException("Неизвестный статус: " + value);
        }
        return result;
    }
}

Использование:

OrderStatus s1 = OrderStatus.fromString("в обработке"); // PROCESSING
OrderStatus s2 = OrderStatus.fromString("SHIPPED");     // SHIPPED
OrderStatus s3 = OrderStatus.fromString("отменён");     // CANCELLED

Дополнительные советы

  • Null safety: Всегда проверяйте входную строку на null
  • Stream API: Для простых случаев можно использовать стримы:
public static Optional<OrderStatus> findByString(String value) {
    return Arrays.stream(values())
        .filter(s -> s.name().equalsIgnoreCase(value))
        .findFirst();
}

Этот подход возвращает Optional, что явно показывает — результат может отсутствовать.

Выбирайте метод в зависимости от ваших потребностей. Для простых случаев достаточно valueOf() с toUpperCase(), для production-кода лучше использовать кастомный метод с Map — он быстрее и гибче.

<<>>

Альтернативный подход: использование аннотации @JsonCreator (для Jackson)

Если вы работаете с JSON (например, в REST API или при десериализации), самый удобный способ — добавить аннотацию @JsonCreator из библиотеки Jackson. Это позволяет автоматически преобразовывать строки из JSON в enum без ручного кода:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

public enum OrderStatus {
    NEW, PROCESSING, SHIPPED, DELIVERED, CANCELLED;

    @JsonCreator
    public static OrderStatus fromString(String value) {
        if (value == null) return null;
        for (OrderStatus status : values()) {
            if (status.name().equalsIgnoreCase(value)) {
                return status;
            }
        }
        throw new IllegalArgumentException("Unknown status: " + value);
    }

    @JsonValue
    public String toValue() {
        return name().toLowerCase();
    }
}

Теперь при десериализации JSON строка "processing" автоматически преобразуется в OrderStatus.PROCESSING, а при сериализации enum превратится в строку "processing". Это особенно удобно для Spring Boot приложений, где Jackson используется "из коробки".

Для простых случаев без внешних библиотек используйте первый ответ — он универсален и не требует дополнительных зависимостей.