Як отримати значення enum зі значення рядка на Java?


1983

Скажіть, у мене є перелік, який справедливий

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

і я хотів би знайти значення enum рядка, наприклад, "A"яке було б Blah.A. Як можна було б це зробити?

Чи потрібний Enum.valueOf()мені метод? Якщо так, то як би я цим скористався?

Відповіді:


2258

Так, Blah.valueOf("A")дадуть тобі Blah.A.

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

Методи статичні valueOf()і values()створюються під час компіляції і не з'являються у вихідному коді. Вони все ж з'являються у Javadoc; наприклад, Dialog.ModalityTypeпоказані обидва способи.


100
Для довідки, Blah.valueOf("A")метод чутливий до регістру і не переносить сторонні пробіли, тому альтернативне рішення, запропоноване нижче @ JoséMi.
Бретт

3
@ Майкл Майерс, Оскільки ця відповідь на сьогодні найбільше проголосується, чи слід розуміти, що добре визначити перерахунок і значення його рядка точно таким же?
Кевін Мередіт

4
@KevinMeredith: Якщо ви маєте на увазі toString()значення, ні, я б не сказав цього. name()отримає вам фактично визначене ім’я константи перерахунку, якщо ви не перекриєте це.
Майкл Майерс

3
Що саме ви маєте на увазі під "створюються під час компіляції та не відображаються у вихідному коді". ?
дереваAreEverywhere

8
@treesAreEverywhere Більш конкретно, ці методи генеруються (або синтезуються ) компілятором. Фактичне enum Blah {...}визначення не повинно намагатися оголосити своїм власним valuesні valuesOf. Це як би ви могли писати "AnyTypeName.class", навіть якщо ви ніколи не оголошували змінну члена "class"; компілятор робить це просто Just Work. (Ця відповідь може бути вам більше не корисною через 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 / ...
Sanghyun Lee

11
@Sangdol Чи не могли б ви порадувати нас, чому повернення нуля краще?
viskiysierra

57
@Sangdol, як правило, добре перевірити, що робить SUN - oops - Oracle у тій же ситуації. І як Enum.valueOf () показує , що IS найкраща практика кинути виняток в цьому випадку. Тому що це виняткова ситуація. "Оптимізація продуктивності" є поганим приводом для написання нечитабельного коду ;-)
raudi

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

Тоді в моєму курсі перерахування зазвичай у мене є це, щоб заощадити текст:

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

Якщо ваші перерахунки не всі обмежувачі, просто змініть Enum.valueOfрядок.

Шкода , я не можу використовувати T.classдля , Enum.valueOfяк Tстирається.


176
Цей порожній блок улову насправді змушує мене вибачити, вибачте.
віскісієра

32
@LazloBonin: Винятки становлять виключні умови, а не контрольний потік. Отримайте собі копію Ефективної Java .
Відновити Моніку - М. Шредер

10
Якщо Java API, який ви хочете використовувати, кидає виняток, і ви не хочете, щоб ваш код кидав такий, ви можете проковтнути виняток, як цей, або переписати логіку з нуля, так що виняток не викидається в першу чергу. Проковтування винятку часто є меншим злом.
Nate CK

47
Жахливо! Завжди завжди вибирайте винятки, де ви можете їх впоратися. Наведений вище приклад - ідеальний приклад, як НЕ робити цього . Чому? Таким чином, він повертає NULL, а потім абонент повинен перевірити чи NULL або кинути NPE. Якщо абонент знає, як впоратися з ситуацією, тоді це робити, якщо vs. try-catch може виглядати трохи елегантніше, АЛЕ якщо він не може впоратися, він повинен знову пропустити нуль, і абонент, який телефонує , повинен знову перевірити NULL і т. д. тощо
raudi

10
Для справедливості до рішення, поданого вище, справді використовуються випадки, коли потрібно повернути нуль замість того, щоб викидати IllegalArgumentException і порушити потік вашої програми, наприклад, відображення перерахунків між схемою веб-сервісу та схемою бази даних, де вони не завжди є одним -до одного. Однак я згоден, що блок ловлі ніколи не повинен залишатися порожнім. Поставте якийсь код на зразок log.warn або щось для відстеження.
Адріан М

101

Використовуйте шаблон із Джошуа Блоха, Ефективна 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 асоціюється з серіалізаційними даними, оскільки це дозволяє контролювати корпус, не даючи Sonar fit.
Новатерата

1
Java 8, безумовно, може / змінить багато (кращих) відповідей на цьому форумі. Не впевнений у тому, що коли-небудь у хвіст (Sonar) буде вішати собаку (код програми).
Даррелл Тіг

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)основі tc. коментар і документи Java

edit2 На android ви повинні використовувати Locale.US, як вказує sulai .


6
Будьте обережні щодо локальної локалізації!
тс.

3
Для вас, користувачів Android, я хотів би зазначити, що документація на Android явно заохочує використання Locale.USдля машиночитаного вводу / виводу.
сулай

2
@Trengot Так, на жаль. Туреччина - хороший приклад. Поєднайте це з несправним поводженням 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
Штійн де Вітт

Як і нечутливість до випадків, але ... віддайте перевагу перерахункам рядків (випадкових) рядків для клавіш та ... другорядних, але повторення є менш ефективними для такого, можливо, повторного пошуку. Звідси impl EnumMap та ін.
Даррелл Тейг

Це не працює! Я змінив значення equalsIgnoreCase на рівне за своїм призначенням. Код не вдався, навіть якщо обидва входи до рівня були абсолютно однаковими.
MasterJoe2

36

Використання Blah.valueOf(string)найкраще, але ви також можете використовувати Enum.valueOf(Blah.class, string).


1
Залежно від регістру, не допомагає!
Муртаза Канчвала

@MurtazaKanchwala Чи можете ви пояснити свій коментар? Що ти намагаєшся зробити?
Пітер Лорі

2
Привіт @PeterLawrey, я намагався отримати Enum з String public enum ObjectType {PERSON ("Person") public String parameterName; ObjectType (String parameterName) {this.parameterName = parameterName; } public String getParameterName () {return this.parameterName; } загальнодоступний статичний ObjectType fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} повернути нуль; }}
Муртаза Канчвала

34

У Java 8 або новіших версіях за допомогою потоків :

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, реалізованих у новіших версіях, неминуче викликає проблеми. Види повинні бути усіма на одній постачальниковій вкладці.
Даррелл Тіг

26

Мої 2 центи тут: використання Java8 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

1
Крім того, щоб дозволити писати більше читабельних switchблоків, ви можете .orElse(null)замість того, .orElseThrow()щоб ви могли зашифрувати виняток у defaultпункті - та включити більше корисної інформації, коли потрібно. І щоб зробити це більш м'яким, ви можете скористатисяv -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())
Адам

або просто повернути Optionalз findFirst(), дозволяючи користувачеві вирішити, чи хоче він .orElse(null), orElseThrow()чи що завгодно ....
user85421

1
Оголошення а public static MyEnum valueOf(String)- це фактично помилка компіляції, оскільки вона така ж, як і неявно визначена, тому стара версія Вашої відповіді насправді краща. ( jls , ideone )
Radiodef

У моєму варіанті краще уникати винятків і використовувати необов’язково. Поруч із цим ми втрачаємо недійсні значення, а також використовуємо Необов’язково.
Ганс Шредер

Знову ж таки, пам’ятайте, що навіть якщо менше чи краще виглядає код ... реалізація потоку на зразок цього є лише ітератором для всіх значень проти пошуку карти (менш ефективною).
Даррелл Тейг

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

Це поверне вам значення за допомогою імені Stringified Enum. Наприклад, якщо ви вкажете "PERSON" у відEEnumName, воно поверне вам значення Enum, тобто "Person"


13

Інший спосіб зробити це за допомогою неявного статичного методу name()Енума. 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 ("МЕРКУРІЯ") поверне Планету. МЕРКУРЮ.

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

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#Otional


Це дійсно гарна відповідь на пов'язані зразки та використання необов’язкових всередині Enum - використовуючи той факт, що Enums теж є класами, і, таким чином, їх можна прикрасити методами, методами переосмислення тощо. Нулі, повернуті з методів, роблять цю конструкцію баггі (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);
    }
}

Джексон - реалізація JSON (JavaScript Object Notation). Первісне питання не мало нічого спільного з JSON.
Даррелл Тейг

Частина JSON була лише предметом бонусу, який я вважав актуальним у той час, оскільки отримання Enum з рядка - це в основному тип десеріалізації, а JSON / Jackson - це, мабуть, найпопулярніше рішення для серіалізації.
Новатерата

Зрозумійте, але з точки зору помірності - це не сприяло відповіді на питання ОП, тому просто намагаюся допомогти встановити контекст. 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;
}

подивіться на це посилання для гідів на відповіді і задавати питання на stackoverflow.com: stackoverflow.com/faq
bakoyaro

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 далі. Зробив би кодування мови, використовуючи стандартні статичні імена та пошук на карті, але все-таки ... хороший приклад наявності перерахунку з різними тегами для того, що є по суті тим же логічним значенням.
Даррелл Тейг

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 має статичну функцію org.apache.commons.lang3.EnumUtils.getEnum, яка буде відображати рядок для вашого типу Enum. Відповідь по суті, як і Джеффріс, але навіщо катати свою, коли вона вже є в дикій природі.


1
Справедливий коментар (DRY), але ... хоча більшість речей Apache Commons є чудовими, я знайшов кілька помилок і анти-моделей в цій базі. Таким чином, посилання на припущення Джошуа Блоха може бути більш сильним. Залишається переглядати код Apache, щоб знати, що хтось реалізував. Якби сказати знаменитий Дуг Лія, який переписав паралельність Java ... тоді я б це довірно довірив.
Даррелл Тейг

4

Додавання відповіді до найвищої оцінки з корисною утилітою ...

valueOf() викидає два різних винятки у випадках, коли це не сподобалось.

  • IllegalArgumentException
  • NullPointerExeption

Якщо ваші вимоги такі, що у вас немає жодної гарантії того, що ваша String обов'язково відповідатиме значення перерахунку, наприклад, якщо дані String надходять із бази даних і можуть містити стару версію enum, вам потрібно буде обробити їх часто ...

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

2

Оскільки switch-версія ще не згадувалася, я її ввожу (повторно використовуючи перерахунок ОП):

  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інтерфейсу, який забезпечує метод, 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", який є унікальним загальнодоступним, а не остаточним і не статичним методом у Foo. У разі , якщо Foo має більш ніж на громадському, а не остаточним і не статичний метод, наприклад, getTranslateякий повертає «драй», інший метод може бути використаний: EnumUtil.from(Foo.class, "drei", "getTranslate").


1

Рішення Котліна

Створіть розширення, а потім зателефонуйте 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

Як ви думаєте, ваша відповідь матиме тут кращий дім? stackoverflow.com/questions/28548015 / ...
nabster

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());
    } 
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.