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

1714

Скажем, у меня есть перечисление, которое просто

public enum Blah {
    A, B, C, D
}

и я хотел бы найти значение перечисления строки, например "A", которая будет Blah.A. Как это можно сделать?

Является ли метод Enum.valueOf() мне нужен? Если да, то как я буду использовать это?

Теги:
enums

24 ответа

1973
Лучший ответ

Да, Blah.valueOf("A") предоставит вам Blah.A.

Обратите внимание, что имя должно быть точным совпадением, включая случай: Blah.valueOf("A") и Blah.valueOf("A ") оба бросают IllegalArgumentException.

Статические методы valueOf() и values() создаются во время компиляции и не отображаются в исходном коде. Однако они появляются в Джавадоке; например, Dialog.ModalityType показывает оба метода.

  • 88
    Для справки: метод Blah.valueOf("A") чувствителен к регистру и не допускает посторонних пробелов, поэтому альтернативное решение, предложенное ниже @ JoséMi, предложено ниже.
  • 2
    @ Майкл Майерс, так как этот ответ является наиболее голосуемым на сегодняшний день, должен ли я понять, что это хорошая практика, чтобы определять enum и его значение String, чтобы они были точно такими же?
Показать ещё 7 комментариев
782

Другое решение, если текст не совпадает с значением перечисления:

public enum Blah {
  A("text1"),
  B("text2"),
  C("text3"),
  D("text4");

  private String text;

  Blah(String text) {
    this.text = text;
  }

  public String getText() {
    return this.text;
  }

  public static Blah fromString(String text) {
    for (Blah b : Blah.values()) {
      if (b.text.equalsIgnoreCase(text)) {
        return b;
      }
    }
    return null;
  }
}
  • 368
    throw new IllegalArgumentException("No constant with text " + text + " found") будет лучше, чем return null .
  • 7
    @ whiskeysierra Джон Скит не согласится с этим. stackoverflow.com/questions/1167982/...
Показать ещё 21 комментарий
100

Вот полезная утилита, которую я использую:

/**
 * A common method for all enums since they can't have another base class
 * @param <T> Enum type
 * @param c enum type. All enums must be all caps.
 * @param string case insensitive
 * @return corresponding enum, or null
 */
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
    if( c != null && string != null ) {
        try {
            return Enum.valueOf(c, string.trim().toUpperCase());
        } catch(IllegalArgumentException ex) {
        }
    }
    return null;
}

Затем в моем классе enum я обычно сохраняю некоторую типизацию:

public static MyEnum fromString(String name) {
    return getEnumFromString(MyEnum.class, name);
}

Если ваши перечисления не все шапки, просто измените строку Enum.valueOf.

Слишком плохо, я не могу использовать T.class для Enum.valueOf, поскольку T стирается.

  • 165
    Этот пустой блок улова действительно сводит меня с ума, извините.
  • 28
    @LazloBonin: Исключения для исключительных условий, а не для потока управления. Получите себе копию Effective Java .
Показать ещё 13 комментариев
66

Вы также должны быть осторожны с вашим делом. Позвольте мне объяснить: выполнение Blah.valueOf("A") работает, но Blah.valueOf("a") не будет работать. Затем снова будет Blah.valueOf("a".toUpperCase(Locale.ENGLISH)).

редактировать
Изменен toUpperCase на toUpperCase(Locale.ENGLISH) на основе tc. комментарий и java-документы

edit2 На андроиде вы должны использовать Locale.US, как указывает сулай.

  • 6
    Будьте осторожны с локалью по умолчанию!
  • 3
    Для вас, пользователей Android, я хотел бы отметить, что документация Android явно рекомендует использовать Locale.US для машиночитаемого ввода / вывода.
Показать ещё 4 комментария
49

Используйте шаблон от Джошуа Блоха, Эффективная Java:

(упрощенный для краткости)

enum MyEnum {
  ENUM_1("A"),
  ENUM_2("B");

  private String name;

  private static final Map<String,MyEnum> ENUM_MAP;

  MyEnum (String name) {
    this.name = name;
  }

  public String getName() {
    return this.name;
  }

  // Build an immutable map of String name to enum pairs.
  // Any Map impl can be used.

  static {
    Map<String,MyEnum> map = new ConcurrentHashMap<String,MyEnum>();
    for (MyEnum instance : MyEnum.values()) {
      map.put(instance.getName(),instance);
    }
    ENUM_MAP = Collections.unmodifiableMap(map);
  }

  public static MyEnum get (String name) {
    return ENUM_MAP.get(name);
  }
}

Также см:

Пример Oracle Java с использованием Enum и карты экземпляров

Порядок выполнения статических блоков в типе Enum

Как я могу просмотреть перечисление Java из его значения String

  • 3
    если Джошуа Блох сказал это, то это единственный путь :-). Обидно, что я всегда должен прокручивать здесь.
  • 5
    Это даже проще в Java 8, чем вы можете сделать: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity())) Я также рекомендую переопределить toString () (передается через конструктор) и использование этого вместо имени, особенно если Enum связан с сериализуемыми данными, так как это позволяет вам контролировать корпус, не подгоняя Sonar.
Показать ещё 9 комментариев
33

Здесь метод, который может сделать это для любого Enum и нечувствителен к регистру.

/** 
 * Finds the value of the given enumeration by name, case-insensitive. 
 * Throws an IllegalArgumentException if no match is found.  
 **/
public static <T extends Enum<T>> T valueOfIgnoreCase(
        Class<T> enumeration, String name) {

    for (T enumValue : enumeration.getEnumConstants()) {
        if (enumValue.name().equalsIgnoreCase(name)) {
            return enumValue;
        }
    }

    throw new IllegalArgumentException(String.format(
        "There is no value with name '%s' in Enum %s",
        name, enumeration.getName()
    ));
}
  • 0
    Этот вариант делает это правильно: equalsIgnoreCase - путь. +1
  • 0
    Подобно нечувствительности к регистру, но ... предпочитают Enums вместо (случайных) строковых назначений для ключей и ... второстепенных, но итерация менее производительна для такого, возможно, повторяющегося поиска. Отсюда вытекает из EnumMap et al.
Показать ещё 1 комментарий
29

Использование Blah.valueOf(string) лучше всего, но вы также можете использовать Enum.valueOf(Blah.class, string).

  • 1
    Чувствителен к регистру, не помогает!
  • 0
    @MurtazaKanchwala Можете ли вы уточнить свой комментарий? Что ты пытаешься сделать?
Показать ещё 1 комментарий
24

Если вы не хотите писать свою собственную утилиту, используйте Google :

Enums.getIfPresent(Blah.class, "A")

В отличие от встроенной функции java, вы можете проверить, присутствует ли A в Blah и не генерирует исключение.

  • 4
    печальная часть, это возвращает Google Необязательно, а не Java Необязательно
  • 0
    Правда. Тем не менее У Google и Netflix есть отличные библиотеки Java. Там, где есть пересечение с классами догоняющего Java, реализованными в более новых версиях, неизбежно возникают проблемы. Вид должен быть олл-ин на одного поставщика lib.
17

Мои 2 цента здесь: использование потоков Java8 + проверка точной строки:

public enum MyEnum {
    VALUE_1("Super"),
    VALUE_2("Rainbow"),
    VALUE_3("Dash"),
    VALUE_3("Rocks");

    private final String value;

    MyEnum(String value) {
        this.value = value;
    }

    /**
     * @return the Enum representation for the given string.
     * @throws IllegalArgumentException if unknown string.
     */
    public static MyEnum fromString(String s) throws IllegalArgumentException {
        return Arrays.stream(MyEnum.values())
                .filter(v -> v.value.equals(s))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("unknown value: " + s));
    }
}

** РЕДАКТИРОВАТЬ **

Переименовав функцию в fromString() так как fromString() ей имя с использованием этого соглашения, вы получите некоторые преимущества от самого языка Java; например:

  1. Прямое преобразование типов в аннотации HeaderParam
  • 1
    В качестве альтернативы, чтобы позволить вам писать более читаемые блоки switch , вы можете использовать .orElse(null) вместо .orElseThrow() чтобы можно было кодировать исключение в предложении по default - и включать больше полезной информации при необходимости. И чтобы сделать его более мягким, вы можете использовать v -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())
  • 0
    или просто вернуть Optional из findFirst() , позволяя пользователю решить, хочет ли он .orElse(null) , или orElseThrow() или что-то еще ....
Показать ещё 1 комментарий
15

В Java 8 или более поздней версии, используя Streams:

public enum Blah
{
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;

    Blah(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public static Blah fromText(String text) {
        return Arrays.stream(values())
          .filter(bl -> bl.text.equalsIgnoreCase(text))
          .findFirst()
          .orElse(null);
    }
}
13

Вам может понадобиться следующее:

public enum ObjectType {
    PERSON("Person");

    public String parameterName;

    ObjectType(String parameterName) {
        this.parameterName = parameterName;
    }

    public String getParameterName() {
        return this.parameterName;
    }

    //From String method will return you the Enum for the provided input string
    public static ObjectType fromString(String parameterName) {
        if (parameterName != null) {
            for (ObjectType objType : ObjectType.values()) {
                if (parameterName.equalsIgnoreCase(objType.parameterName)) {
                    return objType;
                }
            }
        }
        return null;
    }
}

Еще одно дополнение:

   public static String fromEnumName(String parameterName) {
        if (parameterName != null) {
            for (DQJ objType : DQJ.values()) {
                if (parameterName.equalsIgnoreCase(objType.name())) {
                    return objType.parameterName;
                }
            }
        }
        return null;
    }

Это вернет вам значение с помощью стробированного имени перечисления, например. если вы предоставите "ПЕРСОН" в fromEnumName, он вернет вам значение Enum i.e. "Person"

10

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

public enum Blah {

    A, B, C, D;

    public static Blah getEnum(String s){
        if(A.name().equals(s)){
            return A;
        }else if(B.name().equals(s)){
            return B;
        }else if(C.name().equals(s)){
            return C;
        }else if (D.name().equals(s)){
            return D;
        }
        throw new IllegalArgumentException("No Enum specified for this string");
    }
}

Тестирование:

System.out.println(Blah.getEnum("B").name());

//it will print B  B

вдохновение: 10 Примеры Enum в Java

  • 7
    По сути, это то, что valueOf делает для вас. Этот статический метод не предлагает ничего лишнего, исключение и все такое. Тогда конструкции if / else очень опасны ... любая добавленная новая константа перечисления вызовет разрыв этого метода без изменений.
  • 0
    Рассмотрим также этот пример того, как мы можем использовать valueOf для поиска без учета регистра или как мы можем избежать его исключения и использовать псевдонимы для предоставления альтернативных имен: stackoverflow.com/a/12659023/744133
Показать ещё 1 комментарий
7

Решение с использованием библиотек Guava. Метод getPlanet() нечувствителен к регистру, поэтому getPlanet ( "MerCUrY" ) вернет Planet.MERCURY.

package com.universe.solarsystem.planets;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Enums;
import com.google.common.base.Optional;

//Pluto and Eris are dwarf planets, who cares!
public enum Planet {
   MERCURY,
   VENUS,
   EARTH,
   MARS,
   JUPITER,
   SATURN,
   URANUS,
   NEPTUNE;

   public static Planet getPlanet(String name) {
      String val = StringUtils.trimToEmpty(name).toUpperCase();
      Optional <Planet> possible = Enums.getIfPresent(Planet.class, val);
      if (!possible.isPresent()) {
         throw new IllegalArgumentException(val + "? There is no such planet!");
      }
      return possible.get();
   }
}
6

Чтобы добавить к предыдущим ответам и обратиться к некоторым обсуждениям вокруг нулей и NPE, я использую опции Guava для обработки отсутствующих/недействительных случаев. Это отлично подходит для анализа URI/параметров.

public enum E {
    A,B,C;
    public static Optional<E> fromString(String s) {
        try {
            return Optional.of(E.valueOf(s.toUpperCase()));
        } catch (IllegalArgumentException|NullPointerException e) {
            return Optional.absent();
        }
    }
}

Для тех, кто не знает, здесь есть дополнительная информация об исключении null с опцией: https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional

5

В Java 8 статический шаблон карты еще проще и мой предпочтительный метод. Если вы хотите использовать Enum с Jackson, вы можете переопределить toString и использовать это вместо имени, а затем аннотировать с помощью @JsonValue

public enum MyEnum {
    BAR,
    BAZ;
    private static final Map<String, MyEnum> MAP = Stream.of(MyEnum.values()).collect(Collectors.toMap(Enum::name, Function.identity()));
    public static MyEnum fromName(String name){
        return MAP.get(name);
    }
}

public enum MyEnumForJson {
    BAR("bar"),
    BAZ("baz");
    private static final Map<String, MyEnumForJson> MAP = Stream.of(MyEnumForJson.values()).collect(Collectors.toMap(Object::toString, Function.identity()));
    private final String value;

    MyEnumForJson(String value) {
        this.value = value;
    }

    @JsonValue
    @Override
    public String toString() {
        return value;
    }

    public static MyEnumForJson fromValue(String value){
        return MAP.get(value);
    }
}
5
public static MyEnum getFromValue(String value) {
    MyEnum resp = null;
    MyEnum nodes[] = values();
    for(int i = 0; i < nodes.length; i++) {
        if(nodes[i].value.equals(value)) {
            resp = nodes[i];
            break;
        }
    }
    return resp;
}
  • 0
    взгляните на эту ссылку, чтобы узнать, как отвечать и задавать вопросы на stackoverflow.com: stackoverflow.com/faq
  • 1
    Это более или менее совпадает с ответом Хосеми
4

O (1) метод, основанный на извлеченном сгенерированном коде, который использует хэш-карту.

public enum USER {
        STUDENT("jon",0),TEACHER("tom",1);

        private static final Map<String, Integer> map = new HashMap<>();

        static {
                for (USER user : EnumSet.allOf(USER.class)) {
                        map.put(user.getTypeName(), user.getIndex());
                }
        }

        public static int findIndexByTypeName(String typeName) {
                return map.get(typeName);
        }

        private USER(String typeName,int index){
                this.typeName = typeName;
                this.index = index;
        }
        private String typeName;
        private int index;
        public String getTypeName() {
                return typeName;
        }
        public void setTypeName(String typeName) {
                this.typeName = typeName;
        }
        public int getIndex() {
                return index;
        }
        public void setIndex(int index) {
                this.index = index;
        }

}
3

java.lang.Enum определяет несколько полезных методов, доступных для всех типов перечисления в Java:

  • Вы можете использовать метод name(), чтобы получить имя любых констант Enum. Строковым литералом, используемым для записи констант перечисления, является их имя.
  • Аналогично метод values() может быть использован для получения массива всех констант Enum из типа Enum.
  • И для заданного вопроса вы можете использовать метод valueOf() для преобразования константы String в Enum в Java, как показано ниже.
public class EnumDemo06 {
    public static void main(String args[]) {
        Gender fromString = Gender.valueOf("MALE");
        System.out.println("Gender.MALE.name() : " + fromString.name());
    }

    private enum Gender {
        MALE, FEMALE;
    }
}

Output:
Gender.MALE.name() : MALE

В этом фрагменте кода метод valueOf() возвращает константу Enum Gender.MALE, вызывающее имя на которой возвращает "MALE".

2

Добавляя к высокому рейтингу ответ с полезной утилитой...

valueOf() выдает два разных Исключения в случаях, когда он не нравится его ввод.

  • IllegalArgumentException
  • NullPointerExeption

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

Итак, здесь используется метод многократного использования, который позволяет нам определить возвращаемый по умолчанию Enum, если строка String, которую мы передаем, не соответствует.

private static <T extends Enum<T>> T valueOf( String name , T defaultVal) {
        try {
            return Enum.valueOf(defaultVal.getDeclaringClass() , name);
        } catch (IllegalArgumentException | NullPointerException e) {
            return defaultVal;
        }
    }

Используйте его следующим образом:

public enum MYTHINGS {
    THINGONE,
    THINGTWO
}

public static void main(String [] asd) {
  valueOf("THINGTWO" , MYTHINGS.THINGONE);//returns MYTHINGS.THINGTWO
  valueOf("THINGZERO" , MYTHINGS.THINGONE);//returns MYTHINGS.THINGONE
}
2

Apache commons-lang библиотека имеет статическую функцию org.apache.commons.lang3.EnumUtils.getEnum, который отобразит строку в ваш тип Enum. Тот же ответ по существу, что и Geoffreys, но зачем сворачивать свои собственные, когда он уже находится в дикой природе.

1

Поскольку switch -version еще не упоминался, я ввожу его (повторное использование перечисления OP):

  private enum Blah {
    A, B, C, D;

    public static Blah byName(String name) {
      switch (name) {
        case "A":
          return A;
        case "B":
          return B;
        case "C":
          return C;
        case "D":
          return D;
        default:
          throw new IllegalArgumentException(
            "No enum constant " + Blah.class.getCanonicalName() + "." + name);
      }
    }
  }

Поскольку это не дает никакого дополнительного значения методу valueOf(String name), имеет смысл только определить дополнительный метод, если мы хотим иметь другое поведение. Если мы не хотим поднимать IllegalArgumentException мы можем изменить реализацию:

  private enum Blah {
    A, B, C, D;

    public static Blah valueOfOrDefault(String name, Blah defaultValue) {
      switch (name) {
        case "A":
          return A;
        case "B":
          return B;
        case "C":
          return C;
        case "D":
          return D;
        default:
          if (defaultValue == null) {
            throw new NullPointerException();
          }
          return defaultValue;
      }
    }
  }

Предоставляя значение по умолчанию, мы сохраняем контракт Enum.valueOf(String name) не бросая IllegalArgumentException таким образом, чтобы ни в коем случае не возвращался null. Поэтому мы бросаем NullPointerException если имя имеет значение null и в случае default если defaultValue равно null. То, как работает valueOfOrDefault.

Этот подход использует схему Map -Interface, которая предоставляет метод Map.getOrDefault(Object key, V defaultValue) с Java 8.

1

Как насчет?

public enum MyEnum {
    FIRST,
    SECOND,
    THIRD;

    public static Optional<MyEnum> fromString(String value){
        try{
            return Optional.of(MyEnum.valueOf(value));
        }catch(Exception e){
            return Optional.empty();
        }
    }
}
1

Другая утилита, выполняющая обратный путь. Используя значение, которое идентифицирует этот Enum, а не его имя.

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.EnumSet;

public class EnumUtil {

    /**
     * Returns the <code>Enum</code> of type <code>enumType</code> whose a 
     * public method return value of this Enum is 
     * equal to <code>valor</code>.<br/>
     * Such method should be unique public, not final and static method 
     * declared in Enum.
     * In case of more than one method in match those conditions
     * its first one will be chosen.
     * 
     * @param enumType
     * @param value
     * @return 
     */
    public static <E extends Enum<E>> E from(Class<E> enumType, Object value) {
        String methodName = getMethodIdentifier(enumType);
        return from(enumType, value, methodName);
    }

    /**
     * Returns the <code>Enum</code> of type <code>enumType</code> whose  
     * public method <code>methodName</code> return is 
     * equal to <code>value</code>.<br/>
     *
     * @param enumType
     * @param value
     * @param methodName
     * @return
     */
    public static <E extends Enum<E>> E from(Class<E> enumType, Object value, String methodName) {
        EnumSet<E> enumSet = EnumSet.allOf(enumType);
        for (E en : enumSet) {
            try {
                String invoke = enumType.getMethod(methodName).invoke(en).toString();
                if (invoke.equals(value.toString())) {
                    return en;
                }
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }

    private static String getMethodIdentifier(Class<?> enumType) {
        Method[] methods = enumType.getDeclaredMethods();
        String name = null;
        for (Method method : methods) {
            int mod = method.getModifiers();
            if (Modifier.isPublic(mod) && !Modifier.isStatic(mod) && !Modifier.isFinal(mod)) {
                name = method.getName();
                break;
            }
        }
        return name;
    }
}

Пример:

public enum Foo {
    ONE("eins"), TWO("zwei"), THREE("drei");

    private String value;

    private Foo(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

EnumUtil.from(Foo.class, "drei") возвращает Foo.THREE, потому что он будет использовать getValue для соответствия "drei", который является уникальным публичным, а не конечным, а не статическим методом в Foo. В случае, если Foo имеет больше, чем для публичного, а не окончательного и не статического метода, например, getTranslate, который возвращает "drei", можно использовать другой метод: EnumUtil.from(Foo.class, "drei", "getTranslate").

0

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

static <E extends Enum<E>> Enum getEnumValue(String what, Class<E> enumClass) {
    Enum<E> unknown=null;
    for (Enum<E> enumVal: enumClass.getEnumConstants()) {  
        if (what.compareToIgnoreCase(enumVal.name()) == 0) {
            return enumVal;
        }
        if (enumVal.name().compareToIgnoreCase("unknown") == 0) {
            unknown=enumVal;
        }
    }  
    return unknown;
}
  • 0
    Выглядит хорошо, но по сути такой же, как ответ Патрика Арнесена ...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню