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

1984

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

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

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

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

Малахия
источник

Ответы:

2259

Да, Blah.valueOf("A")даст вам Blah.A.

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

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

Майкл Майерс
источник
100
Для справки, Blah.valueOf("A")метод чувствителен к регистру и не допускает посторонних пробелов, поэтому альтернативное решение, предложенное ниже @ JoséMi.
Бретт
3
@ Майкл Майерс, так как этот ответ является самым голосуемым на сегодняшний день, должен ли я понять, что это хорошая практика, чтобы определять enum и его значение String, чтобы они были точно такими же?
Кевин Мередит
4
@KevinMeredith: Если вы имеете в виду toString()значение, нет, я бы так не сказал. name()вы получите фактическое имя константы enum, если вы не переопределите его.
Майкл Майерс
3
Что именно вы подразумеваете под «создаются во время компиляции и не появляются в исходном коде». ?
treesAreEverywhere
8
@treesAreEverywhere Более конкретно, эти методы генерируются (или синтезируются ) компилятором. Фактическое enum Blah {...}определение не должно пытаться объявить свое собственное, valuesни valuesOf. Это похоже на то, как вы можете написать AnyTypeName.class, даже если вы никогда не объявляли переменную-член класса; компилятор делает все это просто работа. (Этот ответ может больше не быть полезным для вас через 3 месяца, но на всякий случай.)
Ti Strga
864

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

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;
    }
}
Хосей
источник
396
throw new IllegalArgumentException("No constant with text " + text + " found")было бы лучше, чем return null.
вискисьерра
8
@ whiskeysierra Джон Скит не согласится с этим. stackoverflow.com/questions/1167982/…
Санхьюн Ли
11
@Sangdol Не могли бы вы рассказать нам, почему возвращать ноль лучше?
вискисьерра
57
@Sangdol обычно полезно проверить, что SUN - упс - Oracle делает в такой же ситуации. И как Enum.valueOf () показывает , что IS лучшая практика бросить исключение в этом случае. Потому что это исключительная ситуация. «Оптимизация производительности» - плохое оправдание для написания нечитаемого кода ;-)
Рауди
5
Ну, вы также можете использовать аннотацию @Nullable, чтобы сделать ее «читабельной» ;-)
JoséMi
121

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

/**
 * 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стирается.

Джеффри Чжэн
источник
176
Этот пустой блок улова действительно сводит меня с ума, извините.
вискисьерра
32
@LazloBonin: исключения для исключительных условий, а не для потока управления. Получите себе копию Effective Java .
Восстановите Монику - М. Шредер
10
Если Java API, который вы хотите использовать, генерирует исключение, и вы не хотите, чтобы ваш код генерировал его, вы можете либо проглотить исключение, как это, либо переписать логику с нуля, чтобы исключение не создавалось в первую очередь. Глотание исключения часто является меньшим злом.
Nate CK
47
Ужасный! Всегда, всегда ловите исключения, где вы можете их обработать. Приведенный выше пример является прекрасным примером того, как НЕ делать этого . Почему? Таким образом, он возвращает NULL, и вызывающий затем должен проверить NULL или бросить NPE. Если вызывающий знает, как справиться с ситуацией, тогда выполнение if vs. try-catch может выглядеть немного более элегантно, НО, если он не может обработать, он должен снова передать значение null, а вызывающий объект снова должен проверить на NULL. и т. д. и т. д.
Рауди
10
Чтобы быть справедливым по отношению к решению, приведенному выше, существуют действительно случаи, когда требуется вернуть null вместо того, чтобы выдавать исключение IllegalArgumentException и прерывать поток вашей программы, например, отображая перечисления между схемой веб-службы и схемой базы данных, где они не всегда едины. -к одному. Однако я согласен с тем, что блок catch никогда не следует оставлять пустым. Поместите некоторый код, например, log.warn или что-то для отслеживания цели.
Адриан М
102

Используйте образец из Джошуа Блоха, Эффективная 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 и Map of instance

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

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

Даррелл Тиг
источник
4
если Джошуа Блох сказал это, то это единственный путь :-). Это позор, что я всегда должен прокручивать здесь.
Дермориц
11
В Java 8 это даже проще, чем вы можете сделать: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity()))я также рекомендую переопределить toString () (передаваемый через конструктор) и использовать его вместо имени, особенно если Enum связан с сериализуемыми данными, так как это позволяет вам управлять регистром без предоставления Сонар в припадке.
Новатерата
1
Java 8, безусловно, может / изменит многие (лучшие) ответы на этом форуме. Не уверен в том, что когда-либо хвастался (Сонар) вилять собакой (код приложения).
Даррелл Тиг
3
Если вы собираетесь поставить его в unmodifiableMapлюбом случае, то нет никакой пользы , начиная с ConcurrentHashMap. Просто используйте HashMap. (Если у вас есть гуава, ImmutableMapто я бы порекомендовал это вместо этого!)
Даниэль Приден
9
Статическая инициализация изначально синхронизирована , поэтому нет абсолютно никакой причины использовать ее ConcurrentHashMapздесь, где карта никогда не изменяется после инициализации. Следовательно, почему даже, например, пример в самом JLS использует регулярный HashMap.
Radiodef
74

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

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

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

Жоао Портела
источник
6
Будьте осторожны с локалью по умолчанию!
тк.
3
Для вас, пользователей Android, я хотел бы отметить, что документация Android явно рекомендует использовать Locale.USдля машиночитаемого ввода / вывода.
Сулай
2
@ Тренго Да, к сожалению. Турция является хорошим примером. Объедините это с неработающей обработкой Java наборов символов по умолчанию (по умолчанию используется латинский в Windows вместо Unicode), и вы обнаружите, что почти всегда небезопасно использовать версии методов по умолчанию, которые принимают кодировку или локаль. Вы должны почти всегда явно определять их.
Стейн де Витт
Не уверен, что наборы символов по умолчанию в Java и др. «Сломаны» сами по себе, но предоставлены, по умолчанию вместо UTF-8 вместо переопределений (которые должны быть сделаны всегда, чтобы быть явными), были бы лучшие системы для начинающих программистов, которые обычно не понимают концепции кодировки.
Даррелл Тиг
38

Вот метод, который может сделать это для любого 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()
    ));
}
Патрик Арнесен
источник
Этот вариант делает это правильно: equalsIgnoreCaseэто путь. +1
Стейн де Витт
Подобно нечувствительности к регистру, но ... предпочитают Enums вместо (случайных) строковых назначений для ключей и ... второстепенных, но итерация менее производительна для такого возможно повторного поиска. Отсюда вытекает из EnumMap et al.
Даррелл Тиг
Это не работает ! Я изменил equalsIgnoreCase на equals для моей цели. Код не удался, даже если оба входа равны были одинаковыми.
MasterJoe2
36

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

Питер Лори
источник
1
Чувствителен к регистру, не помогает!
Муртаза Канчвала
@MurtazaKanchwala Можете ли вы уточнить свой комментарий? Что ты пытаешься сделать?
Питер Лоури
2
Привет @PeterLawrey, я пытался получить Enum из открытого перечисления String ObjectType {PERSON ("Person") public String parameterName; ObjectType (String параметрName) {this.parameterName = параметрName; } public String getParameterName () {return this.parameterName; } public static ObjectType fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} return null; }}
Муртаза Канчвала
35

В 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 Optional<Blah> fromText(String text) {
        return Arrays.stream(values())
          .filter(bl -> bl.text.equalsIgnoreCase(text))
          .findFirst();
    }
}
Ханс Шройдер
источник
Не уверен, что это как-то лучше ответ. Потоки в этом случае являются однопоточными итераторами, как и любые другие по всем значениям, и, возможно, менее производительными, чем подразумеваемый просмотр карты. Потоки имеют большую ценность в многопоточном контексте, где, например, параллельное выполнение разделенного новой строкой текстового файла может улучшить производительность.
Даррелл Тиг
28

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

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

В отличие от встроенной функции Java, она позволяет вам проверить, присутствует ли A в Blah и не выдает ли исключение.

Андрейс
источник
7
грустная часть, это возвращает Google Необязательный, а не Java Необязательный
javaProgrammer
Правда. Тем не менее У Google и Netflix есть отличные библиотеки Java. Там, где есть пересечение с классами догоняющего Java, реализованными в более новых версиях, неизбежно возникают проблемы. Вид должен быть олл-ин на одного поставщика lib.
Даррелл Тиг
26

Мои 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()с тех пор как она была названа с использованием этого соглашения, вы получите некоторые преимущества от самого языка Java; например:

  1. Прямое преобразование типов в аннотации HeaderParam
Manu
источник
1
В качестве альтернативы, чтобы вы могли писать более читаемые switchблоки, вы можете .orElse(null)вместо .orElseThrow()этого кодировать исключение, выбрасываемое в defaultпредложении, и включать больше полезной информации, когда это необходимо. И чтобы сделать его более снисходительным, вы можете использоватьv -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())
Адам
или просто вернуть Optionalиз findFirst(), что позволяет пользователю решить , хочет ли он .orElse(null), orElseThrow()или что - то ....
user85421
1
Объявление a public static MyEnum valueOf(String)на самом деле является ошибкой компиляции, поскольку оно совпадает с неявно определенным, поэтому старая версия вашего ответа на самом деле лучше. ( JLS , ideone )
Radiodef
В моем варианте лучше избегать исключений и использовать опционально. Кроме того, мы должны аннулировать null и использовать Optional.
Ганс
Опять же, просто помните, что даже если код выглядит хуже или лучше ... реализация Stream, подобная этой, является просто итератором для всех значений по сравнению с поиском на карте (менее производительным).
Даррелл Тиг
16

Вам может понадобиться это:

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;
    }

Это вернет вам значение по строковому имени Enum. Например, если вы укажете «PERSON» в fromEnumName, он вернет вам значение Enum, то есть «Person»

Муртаза Канчвала
источник
13

Еще один способ сделать это с помощью неявного статического метода 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 очень опасны ... любая добавленная новая константа перечисления вызовет разрыв этого метода без изменений.
YoYo
Рассмотрим также этот пример того, как мы можем использовать valueOf для поиска без учета регистра или как избежать его исключения и использовать псевдонимы для предоставления альтернативных имен: stackoverflow.com/a/12659023/744133
YoYo
2
name()не является статичным
nrubin29
10

Решение с использованием библиотек 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();
   }
}
javabrew
источник
8

Чтобы добавить к предыдущим ответам и обсудить некоторые дискуссии вокруг нулей и 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();
        }
    }
}

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

Том
источник
Это действительно хороший ответ для связанных шаблонов и использования Optional внутри Enum - используя тот факт, что Enums тоже являются классами и, следовательно, могут быть украшены методами, методами переопределения и т. Д. Это хороший случай для программирования в стиле Fluent, так как нули, возвращаемые из методов, делают эту конструкцию ошибочной (NPE в неизвестных местах в цепочках вызовов методов Fluent).
Даррелл Тиг
8

В Java 8 шаблон статической карты еще проще и является моим предпочтительным методом. Если вы хотите использовать Enum с Джексоном, вы можете переопределить 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);
    }
}
Novaterata
источник
Джексон - это реализация JSON (JavaScript Object Notation). Первоначальный вопрос не имел ничего общего с JSON.
Даррелл Тиг
Часть JSON была просто бонусным материалом, который мне показался актуальным в то время, так как получение Enum из строки - это, по сути, тип десериализации, а JSON / Jackson, вероятно, является самым популярным решением для сериализации.
Новатерата
Поймите, но с точки зрения модерации - это не способствовало ответу на вопрос OP, поэтому просто попытка помочь установить контекст там. JSON - действительно способ преобразования объектов в каноническую форму в Java с Джексоном, являющимся отличной библиотекой.
Даррелл Тиг
6
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;
}
Prasobh.Kollattu
источник
Посмотрите эту ссылку, чтобы
узнать, как
1
Это более или менее такое же , как ответ Хосого в
Rup
6

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;
        }

}
Сизиф
источник
Обратите внимание, что нулевой (0) и один (1) идентификаторы не нужны. Метод Enum values ​​() возвращает элементы в том же порядке, что и кодированный. Таким образом, первая запись будет порядковым нулем, вторая и т. Д.
Даррелл Тиг
6

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

public enum Status {

    ACT(new String[] { "Accepted", "مقبول" }),
    REJ(new String[] { "Rejected", "مرفوض" }),
    PND(new String[] { "Pending", "في الانتظار" }),
    ERR(new String[] { "Error", "خطأ" }),
    SNT(new String[] { "Sent", "أرسلت" });

    private String[] status;

    public String getDescription(String lang) {
        return lang.equals("en") ? status[0] : status[1];
    }

    Status(String[] status) {
        this.status = status;
    }
}

И затем вы можете получить описание динамически на основе кода языка, переданного getDescription(String lang)методу, например:

String statusDescription = Status.valueOf("ACT").getDescription("en");
Эбрахим Алраби
источник
1
Хороший пример с продвижением Enums дальше. Сделал бы кодирование языка, используя стандартные статические имена и поиск в Map, но все же ... хороший пример наличия перечисления с разными тегами для того, что по сути является одним и тем же логическим значением.
Даррелл Тиг
5

Что о?

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();
        }
    }
}
ИДК
источник
4

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

  • Вы можете использовать name()метод, чтобы получить имя любой константы Enum. Строковый литерал, используемый для записи констант 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".

КНУ
источник
4

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

pjklauser
источник
1
Честный комментарий (СУХОЙ), но ... хотя большинство вещей из Apache Commons великолепны, я обнаружил в этой базе несколько ошибок и анти-паттернов. Таким образом, ссылаясь на то, что реализация Джошуа Блоха может быть более сильной. Затем приходится пересматривать код Apache, чтобы узнать, что кто-то реализовал. Если бы это был, скажем, знаменитый Даг Лия, который переписал параллелизм Java ... тогда я бы доверял этому безоговорочно.
Даррелл Тиг
4

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

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

  • IllegalArgumentException
  • NullPointerExeption

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

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

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
}
lance.dolan
источник
2

Поскольку switch-версия еще не упоминалась, я представляю ее (повторно используя перечисление 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

Еще одна утилита захвата в обратном порядке. Использование значения, которое идентифицирует этот 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», который является уникальным public, не final и не статическим методом в Foo. В случае , если Foo имеет более чем на общественном, а не окончательным и не статический метод, например, getTranslateкоторый возвращает «драй», другой метод может быть использован: EnumUtil.from(Foo.class, "drei", "getTranslate").

Moesio
источник
1

Kotlin Solution

Создайте расширение, а затем позвоните valueOf<MyEnum>("value"). Если тип недействителен, вы получите ноль и должны обработать его

inline fun <reified T : Enum<T>> valueOf(type: String): T? {
    return try {
        java.lang.Enum.valueOf(T::class.java, type)
    } catch (e: Exception) {
        null
    }
}

Кроме того, вы можете установить значение по умолчанию, вызывая valueOf<MyEnum>("value", MyEnum.FALLBACK)и избегая нулевого ответа. Вы можете расширить свой конкретный enum, чтобы по умолчанию он был автоматическим

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T {
    return try {
        java.lang.Enum.valueOf(T::class.java, type)
    } catch (e: Exception) {
        default
    }
}

Или, если вы хотите оба, сделайте второе:

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T = valueOf<T>(type) ?: default
Gibolt
источник
Как вы думаете, ваш ответ будет лучше дома здесь? stackoverflow.com/questions/28548015/…
Набстер
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;
}
Джон Хемминг
источник
-1

Самый быстрый способ получить имя enum - это создать карту текста и значения enum при запуске приложения и получить имя, вызвав функцию Blah.getEnumName ():

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

    private String text;
    private HashMap<String, String> map;
    Blah(String text) {
    this.text = text;
    }

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

    static{
      createMapOfTextAndName();
    }

    public static void createMapOfTextAndName() {
        map = new HashMap<String, String>();
        for (Blah b : Blah.values()) {
             map.put(b.getText(),b.name());
        }
    }
    public static String getEnumName(String text) {
        return map.get(text.toLowerCase());
    } 
}
Бишал Джайсвал
источник