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

2163

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

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

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

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

0
2447

Да, Blah.valueOf("A")дам тебе Blah.A.

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

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

11
  • 108
    Для справки, Blah.valueOf("A")метод чувствителен к регистру и не допускает лишних пробелов, поэтому альтернативное решение, предложенное ниже @ JoséMi. Brett 17 дек.
  • 3
    @Michael Myers, поскольку этот ответ получил наибольшее количество голосов, должен ли я понимать, что это хорошая практика, чтобы определить перечисление и его значение String, чтобы они были точно такими же? Kevin Meredith 12 фев '14 в 21:24
  • 4
    @KevinMeredith: Если вы имеете в виду toString()ценность, нет, я бы этого не сказал. name()даст вам фактическое определенное имя константы перечисления, если вы не переопределите его. Michael Myers 14 фев '14 в 4:10
  • 4
    Что именно вы имеете в виду под словами «создаются во время компиляции и не отображаются в исходном коде». ? treesAreEverywhere 7 марта '14 в 0:43
  • 8
    @treesAreEverywhere В частности, эти методы генерируются (или синтезируются ) компилятором. Фактическое enum Blah {...}определение не должно пытаться объявить его самостоятельно , valuesни valuesOf. Это похоже на то, как вы можете написать AnyTypeName.class, даже если вы на самом деле никогда не объявляли переменную-член «class»; компилятор заставляет все это просто работать. (Этот ответ может вам больше не пригодиться через 3 месяца, но на всякий случай.)Ti Strga 30 мая '14 в 22: 312014-05-30 19:31
949

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

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;
    }
}
23
  • 434
    throw new IllegalArgumentException("No constant with text " + text + " found")было бы лучше чем return null. whiskeysierra 31 июля '10 в 13: 282010-07-31 10:28
  • 8
    @whiskeysierra Джон Скит не согласился бы с этим. stackoverflow.com/questions/1167982/…Sanghyun Lee 5 сен.
  • 14
    @Sangdol Не могли бы вы объяснить нам, почему возвращать null лучше? whiskeysierra 29 сен.
  • 63
    @Sangdol обычно полезно проверить, что SUN - ой - Oracle делает в той же ситуации. И как Enum.valueOf () показывает , что IS лучшая практика бросить исключение в этом случае. Потому что это исключительная ситуация. «Оптимизация производительности» - плохой предлог для написания нечитаемого кода ;-)raudi 2 фев '12 в 7:43
  • 5
    Ну, вы также можете использовать аннотацию @Nullable, чтобы сделать ее «читаемой» ;-)JoséMi 3 фев '12 в 8:20
134

Используйте шаблон Джошуа Блоха , Эффективная 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().toLowerCase(),instance);
        }
        ENUM_MAP = Collections.unmodifiableMap(map);
    }

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

Также см:

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

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

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

16
  • 7
    если это сказал Джошуа Блох, то это единственный выход :-). Жалко, что мне всегда приходится прокручивать здесь вниз. dermoritz 10 янв.
  • 14
    В Java 8 это еще проще, чем вы: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity()))я также рекомендую переопределить toString () (переданный через конструктор) и использовать это вместо имени, особенно если Enum связан с сериализуемыми данными, поскольку это позволяет вам управлять корпусом, не давая Сонар подходит. Novaterata 27 апр '17 в 17:01
  • 1
    Java 8, безусловно, может / изменит многие (лучшие) ответы на этом форуме. Не уверен, что когда-нибудь хвост (сонар) виляет собакой (код приложения). Darrell Teague 28 апр '17 в 20:31
  • 4
    Если вы все unmodifiableMapравно собираетесь использовать его , то начинать с ConcurrentHashMap. Просто используйте HashMap. (Если у вас есть Guava, ImmutableMapя бы рекомендовал это вместо этого!)Daniel Pryden 8 марта '18 в 16: 132018-03-08 16:13
  • 11
    Статическая инициализация по своей сути синхронизирована , поэтому нет абсолютно никаких причин для использования ConcurrentHashMapздесь, где карта никогда не изменяется после инициализации. Следовательно, почему даже, например, пример в самом JLS использует обычный HashMap. Radiodef 5 авг.
130

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

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

Затем в моем классе перечисления у меня обычно есть это, чтобы сэкономить на вводе:

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

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

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

15
  • 188
    Этот пустой блокиратор действительно сводит меня с ума, извините. whiskeysierra 31 июля '10 в 13: 142010-07-31 10:14
  • 33
    @LazloBonin: исключения предназначены для исключительных условий, а не для потока управления. Получите себе копию Эффективной Java . Martin Schröder 15 нояб.
  • 10
    Если Java API, который вы хотите использовать, выдает исключение, и вы не хотите, чтобы ваш код генерировал его, вы можете либо проглотить подобное исключение, либо переписать логику с нуля, чтобы исключение не генерировалось в первую очередь. Проглатывание исключения часто оказывается меньшим злом. Nate C-K 30 нояб.
  • 47
    Ужасный! Всегда, всегда перехватывайте исключения там, где вы можете их обработать. Приведенный выше пример является прекрасным примером того, как этого НЕ делать . Почему? Таким образом, он возвращает NULL, и вызывающий затем должен проверить на NULL или выдать NPE. Если вызывающий знает, как справиться с ситуацией, то выполнение if vs. try-catch может выглядеть немного более элегантно, НО, если он не может справиться, он должен снова передать null, а вызывающий абонент снова должен проверить против NULL и т. д. и т. д.raudi 2 фев '12 в 7:52
  • 10
    Чтобы быть справедливым по отношению к вышеприведенному решению, действительно существуют случаи, когда вы должны возвращать null вместо того, чтобы бросать IllegalArgumentException и нарушать поток вашей программы, например, сопоставление перечислений между схемой веб-службы и схемой базы данных, где они не всегда являются одним целым -к одному. Однако я согласен с тем, что блок catch никогда не следует оставлять пустым. Поместите какой-нибудь код, например log.warn или что-то в этом роде, для отслеживания. Adrian M 6 фев '12 в 8:19
78

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

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

4
  • 6
    Остерегайтесь языкового стандарта по умолчанию! tc. 28 нояб.
  • 4
    Для вас, пользователей Android, я хотел бы указать, что документация Android явно поощряет использование Locale.USдля машиночитаемого ввода / вывода. sulai 02 сен '13 в 17:44
  • 2
    @Trengot Да, к сожалению. Турция - хороший тому пример. Объедините это с неправильной обработкой кодировок по умолчанию в Java (в Windows по умолчанию используется Latin вместо Unicode), и вы обнаружите, что почти всегда небезопасно использовать версии методов по умолчанию, которые принимают кодировку или языковой стандарт. Вы должны почти всегда явно определять их. Stijn de Witt 26 июл.
  • Не уверен, что кодировки Java "по умолчанию" и другие "сломаны" сами по себе, но предоставлены, использование по умолчанию UTF-8 вместо переопределений (что всегда должно быть явным) сделало бы системы лучше для младших программистов, которые обычно не понимают концепции кодировки. Darrell Teague 29 мая '18 в 20:45
48

В 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();
    }
}
2
  • Не уверен, что это лучший ответ. Потоки в этом случае представляют собой однопоточный итератор, как и любой другой по всем значениям, и, возможно, менее производительны, чем имплицит поиска по карте. Потоки имеют большее значение в многопоточном контексте, где, например, параллельное выполнение текстового файла, разделенного новой строкой, может повысить производительность. Darrell Teague 15 апр '20 в 22:03
  • @DarrellTeague Если вы измеряете разницу в наносекундах между решением Streams и решением поиска по карте, вы увидите разницу. Однако я считаю, что в реальном мире не будет заметной разницы в производительности, если у вас нет довольно большого перечисления, что, на мой взгляд, является исключением из правила. Кроме того, подход Streams компактен и удобочитаем, хотя я уверен, что вы могли бы привести тот же аргумент и в отношении реализации картыFlaom 15 янв в 20:59
40

Вот метод, который может сделать это для любого 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()
    ));
}
3
  • 1
    Этот вариант делает это правильно: это правильный equalsIgnoreCaseпуть. +1Stijn de Witt 26 июля '15 в 13:35
  • Подобно нечувствительности к регистру, но ... предпочитаю перечисления вместо (случайных) строковых назначений для ключей и ... второстепенных, но итерация менее эффективна для такого, возможно, повторяющегося поиска. Следовательно, согласно EnumMap et al. Darrell Teague 13 авг.
  • Это не работает ! Я изменил equalsIgnoreCase на equals для моей цели. Код не удался, несмотря на то, что оба входа равны были совершенно одинаковыми. MasterJoe 26 ноя '18 в 20:49
37

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

3
  • 1
    С учетом регистра, не помогает! Murtaza Kanchwala 28 июня '15 в 18:58
  • @MurtazaKanchwala Не могли бы вы уточнить свой комментарий? Что ты пытаешься сделать? Peter Lawrey 29 июн.
  • 2
    Привет, @PeterLawrey, я пытался получить Enum из публичного перечисления String ObjectType {PERSON ("Person") public String parameterName; Тип объекта (String имя параметра) {this.parameterName = имя параметра; } общедоступная строка getParameterName () {return this.parameterName; } общедоступный статический ObjectType fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} return null; }}Murtaza Kanchwala 29 июня '15 в 17:45
33

Мои два цента здесь: использование Java 8 Streams и проверка точной строки:

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
5
  • 1
    В качестве альтернативы, чтобы позволить вам писать больше читаемых switchблоков, вы можете .orElse(null)вместо .orElseThrow()этого закодировать выброс исключения в defaultпредложении - и при необходимости включить более полезную информацию. И чтобы сделать его более мягким, вы можете использоватьv -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())Adam 10 мая '18 в 11:35
  • или просто верните Optionalfrom findFirst(), позволяя пользователю решить, хочет ли он .orElse(null), orElseThrow()или что-то еще ....user85421 02 авг.
  • 1
    Объявление a public static MyEnum valueOf(String)на самом деле является ошибкой компиляции, поскольку оно совпадает с неявно определенным, поэтому более старая версия вашего ответа на самом деле лучше. ( jls , ideone )Radiodef 23 дек '18 в 23:34
  • В моем варианте лучше избегать исключений и использовать Optional. Рядом с этим мы должны аннулировать null и вместо этого использовать Optional. Hans Schreuder 23 мая '19 в 10:57
  • Опять же, просто помните, что даже если код выглядит менее или лучше ... реализация Stream, подобная этой, является просто итератором по всем значениям, а не поиском по карте (менее производительным). Darrell Teague 15 апр '20 в 22:05
30

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

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

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

2
  • 7
    печальная часть, это возвращает google Optional, а не java OptionaljavaProgrammer 19 авг.
  • Правда. Однако исключено. У Google и Netflix есть отличные библиотеки Java. Если есть перекрытие с классами догоняющего развития Java, реализованными в более новых версиях, неизбежно возникают проблемы. Вроде бы все на одной библиотеке поставщика. Darrell Teague 13 авг.
17

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

public enum ObjectType {
    PERSON("Person");

    public String parameterName;

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

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

    // From the String method, it 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;
    }

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

0
14

Другой способ сделать это - использовать неявный статический метод 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

3
  • 7
    По сути, это то, valueOfчто вам нужно. Этот статический метод не предлагает ничего лишнего, исключение и все такое. Тогда конструкции if / else очень опасны ... любая добавленная новая константа перечисления приведет к тому, что этот метод сломается без изменений. YoYo 30 сен '12 в 18:01
  • Рассмотрим также этот пример того, как мы можем использовать valueOf для поиска без учета регистра или как мы можем избежать этого исключения и использовать псевдонимы для предоставления альтернативных имен: stackoverflow.com/a/12659023/744133YoYo 30 сен '12 в 18:18
  • 2
    name()не статичен. nrubin29 21 апр '14 в 0:39
12

Вот решение с использованием библиотек 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();
   }
}
1
  • А как насчет Плутона ?! Priidu Neemre 11 мар в 7:56
9

В 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);
    }
}
3
  • Джексон - это реализация JSON (JavaScript Object Notation). Исходный вопрос не имел ничего общего с JSON. Darrell Teague 15 апр '20 в 22:06
  • Часть JSON была просто бонусом, который я считал актуальным в то время, поскольку получение Enum из строки в основном является типом десериализации, а JSON / Jackson, вероятно, является самым популярным решением для сериализации. Novaterata 16 апр '20 в 1:38
  • Поймите, но с точки зрения модерации - это не способствовало ответу на вопрос OP, поэтому просто пытаюсь помочь установить там контекст. JSON действительно является способом преобразования объектов в каноническую форму в Java, а Джексон - отличная библиотека. Darrell Teague 17 апр '20 в 12:50
9

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
  • 1
    Хороший пример с продвижением Enums дальше. Можно было бы сделать языковую кодировку с использованием стандартных статических имен и поиска на карте, но все же ... хороший пример наличия перечисления с разными тегами для того, что по сути является одним и тем же логическим значением. Darrell Teague 15 апр '20 в 22:11
8

Чтобы добавить к предыдущим ответам и обратиться к некоторым обсуждениям вокруг нулей и NPE, я использую Guava Optionals для обработки отсутствующих / недопустимых случаев. Это отлично подходит для анализа 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 с помощью Optional .

1
  • Это действительно хороший ответ для связанных шаблонов и использования Optional внутри Enum - используя тот факт, что Enums также являются классами и, следовательно, могут быть украшены методами, методами переопределения и т. Д. Это хороший случай для программирования в стиле Fluent, поскольку Нулевые значения, возвращаемые методами, делают эту конструкцию ошибочной (NPE в неизвестных местах в цепочках вызовов методов Fluent). Darrell Teague 15 апр '20 в 22:09
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;
}
3
  • 1
    Взгляните на эту ссылку, чтобы найти руководства по ответам и заданию вопросов на stackoverflow.com: stackoverflow.com/faqbakoyaro 9 нояб.
  • 2
    Это более или менее похоже на ответ ХосеМиRup 14 нояб.
  • Объяснение было бы в порядке. Пожалуйста, ответьте, отредактировав (изменив) свой ответ , а не здесь, в комментариях ( без «Изменить:», «Обновить:» и т. Д. - ответ должен появиться так, как если бы он был написан сегодня). Peter Mortensen 10 августа в 17:39
6

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

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

}
1
  • 1
    Обратите внимание, что идентификаторы нуля (0) и единицы (1) не нужны. Метод Enum values ​​() вернет элементы в том же порядке, в котором они были закодированы. Таким образом, первая запись будет иметь порядковый номер ноль, вторая и т. Д.Darrell Teague 15 апр '20 в 22:13
5

В библиотеке Apache commons-lang есть статическая функция org.apache.commons.lang3.EnumUtils.getEnum, которая сопоставляет String с вашим типом Enum. По сути, тот же ответ, что и у Джеффри Чжэна , но нет необходимости катить свой собственный, когда он уже находится в дикой природе.

2
  • 1
    Справедливый комментарий (СУХОЙ), но ... хотя большая часть материала Apache Commons великолепна, я сам обнаружил несколько ошибок и анти-шаблонов в этой базе. Таким образом, можно сказать, что реализация Джошуа Блоха может иметь более прочную основу. Переходим к тому, чтобы просмотреть код Apache, чтобы узнать, что кто-то реализовал. Если бы, скажем, знаменитый Дуг Лиа переписал параллелизм в Java ... тогда я бы безоговорочно ему доверял. Darrell Teague 15 апр '20 в 22:16
  • Первая ссылка не работает: «Не найден. Запрошенный URL не найден на этом сервере». Peter Mortensen 10 августа в 17:53
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. Строковый литерал, используемый для записи констант перечисления, - это их имя.
  • Точно так же 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

Добавив к ответу Майкла Майерса полезную утилиту ...

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

  • IllegalArgumentException
  • NullPointerExeption

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

Итак, вот метод многократного использования, который я написал, который позволяет нам определить 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
}
3

Поскольку 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.

2
  • switchи ООП ? Peter Mortensen 10 августа в 18:04
  • @PeterMortensen: Вопрос OP не ограничивается ООП. Имеется в виду Java. LuCio 11 августа в 6:12
2

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

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 valueOfCode(String blahCode) throws IllegalArgumentException {
    Blah blah = Arrays.stream(Blah.values())
            .filter(val -> val.name().equals(blahCode))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("Unable to resolve blah: " + blahCode));

    return blah;
}

}

1
  • Я не думаю, что это даже компилируется (выделение синтаксиса также дает подсказку). Peter Mortensen 10 августа в 18:09
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который возвращает «драй», другой метод может быть использован: EnumUtil.from(Foo.class, "drei", "getTranslate").

1

Enum valueOf ()

Класс перечисления автоматически получает статический метод valueOf () в классе при компиляции. Метод valueOf () можно использовать для получения экземпляра класса enum для заданного значения String.

Например:

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println(Strings.TWO.name());
    }
    enum Strings {
        ONE, TWO, THREE
    }
}
1
0

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

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
  • Что он должен делать? Как это работает? В чем суть / идея? Пожалуйста, ответьте, отредактировав (изменив) свой ответ , а не здесь, в комментариях ( без «Изменить:», «Обновить:» и т. Д. - ответ должен появиться так, как если бы он был написан сегодня). Peter Mortensen 10 августа в 17:58
0

Котлин Решение

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

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)и избегая нулевого ответа. Вы можете расширить свое конкретное перечисление, чтобы значение по умолчанию было автоматическим

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
3
  • Как вы думаете, ваш ответ будет здесь лучше? stackoverflow.com/questions/28548015/…nabster 5 мая '20 в 22:39
  • 1
    Это не вопрос Котлина. shinzou 18 июн.
  • 1
    Это похоже на Js / jQuery. Когда разработчики Android ищут решение, они ищут Java, а затем переводят на Kotlin. Это неочевидное, лучшее решение, чем прямой перевод. Поисковые системы не собираются в ближайшее время отказываться от этого решения, лучше просто помочь разработчикам быстро найти хорошее решение. Он отмечен как Kotlin, разработчики Java могут просто пропуститьGibolt 18 июня '20 в 16: 062020-06-18 16:06
0

Самый быстрый способ получить имя перечисления - создать карту текста и значений перечисления при запуске приложения, а для получения имени вызвать функцию 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());
    }
}
0

Blah.valueOf("A")- это утверждение, которое вы ищете, но имейте в виду, что это ЧУВСТВИТЕЛЬНО СЛУЧАЙНО, поэтому Blah.valueOf ("a") не будет работать и будет генерировать исключение.