Jak uzyskać wartość wyliczenia z wartości ciągu w Javie?


1982

Powiedz, że mam wyliczenie, które jest sprawiedliwe

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

i chciałbym znaleźć wartość wyliczenia łańcucha, na przykład, "A"która byłaby Blah.A. Jak byłoby to możliwe?

Czy Enum.valueOf()potrzebuję metody? Jeśli tak, jak miałbym to wykorzystać?

Odpowiedzi:


2258

Tak, Blah.valueOf("A")dam ci Blah.A.

Zauważ, że nazwa musi być dokładnie zgodna, w tym wielkość liter: Blah.valueOf("a")i Blah.valueOf("A ")oba rzucają an IllegalArgumentException.

Metody statyczne valueOf()i values()są tworzone w czasie kompilacji i nie pojawiają się w kodzie źródłowym. Pojawiają się jednak w Javadoc; na przykład Dialog.ModalityTypepokazuje obie metody.


100
Dla porównania, w Blah.valueOf("A")metodzie rozróżniana jest wielkość liter i nie toleruje obcych białych znaków, dlatego alternatywne rozwiązanie zaproponowane poniżej przez @ JoséMi.
Brett,

3
@Michael Myers, skoro ta odpowiedź jest najczęściej głosowana, czy powinienem zrozumieć, że dobrą praktyką jest definiowanie wyliczenia i jego wartości ciągu jako dokładnie takiej samej?
Kevin Meredith

4
@KevinMeredith: Jeśli masz na myśli toString()wartość, nie, nie powiedziałbym tego. name()dostanie ci faktycznie zdefiniowaną nazwę stałej wyliczeniowej, chyba że ją zastąpisz.
Michael Myers

3
Co dokładnie rozumiesz przez „są tworzone w czasie kompilacji i nie pojawiają się w kodzie źródłowym”. ?
treesAreEverywhere

8
@treesAreEverywhere Dokładniej, metody te są generowane (lub syntezowane ) przez kompilator. Rzeczywista enum Blah {...}definicja nie powinna próbować deklarować własnej valuesani valuesOf. To tak, jak możesz napisać „AnyTypeName.class”, nawet jeśli tak naprawdę nigdy nie zadeklarowałeś zmiennej „class”; kompilator sprawia, że ​​wszystko działa. (Ta odpowiedź może nie być już przydatna 3 miesiące później, ale na wszelki wypadek.)
Ti Strga

864

Inne rozwiązanie, jeśli tekst nie jest taki sam jak wartość wyliczenia:

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")byłoby lepiej niż return null.
whiskeysierra

8
@ Whiskeysierra Jon Skeet nie zgodziłby się z tym. stackoverflow.com/questions/1167982/...
Sanghyun Lee

11
@Sangdol Czy możesz nam wyjaśnić, dlaczego powrót do wartości null jest lepszy?
whiskeysierra

57
@Sangdol zazwyczaj dobrze jest sprawdzić, co SUN - ups - Oracle robi w tej samej sytuacji. I jak Enum.valueOf () pokazuje on JEST najlepszych praktyk, aby rzucić wyjątek w tym przypadku. Ponieważ jest to wyjątkowa sytuacja. „Optymalizacja wydajności” jest złym usprawiedliwieniem do pisania nieczytelnego kodu ;-)
raudi

5
Cóż, możesz również użyć adnotacji @Nullable, aby uczynić ją „czytelną” ;-)
JoséMi

121

Oto fajne narzędzie, którego używam:

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

Następnie w mojej klasie enum zwykle mam to, aby zaoszczędzić trochę pisania:

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

Jeśli twoje wyliczenia nie są wszystkimi ograniczeniami, po prostu zmień Enum.valueOflinię.

Szkoda, że nie można używać T.classna Enum.valueOfco Tzostaną skasowane.


176
Przepraszam, ten pusty blok catch naprawdę doprowadza mnie do szału.
whiskeysierra

32
@ LazloBonin: Wyjątki dotyczą wyjątkowych warunków, a nie kontroli przepływu. Zdobądź kopię Effective Java .
Przywróć Monikę - M. Schröder

10
Jeśli interfejs API języka Java, którego chcesz użyć, zgłasza wyjątek i nie chcesz, aby kod go zgłaszał, możesz albo połknąć wyjątek w ten sposób, albo ponownie napisać logikę od zera, aby żaden wyjątek nie został zgłoszony. Połknięcie wyjątku jest często mniejszym złem.
Nate CK

47
Okropny! Zawsze zawsze wychwytuj wyjątki, w których możesz sobie z nimi poradzić. Powyższy przykład jest doskonałym przykładem, jak tego NIE robić . Dlaczego? Zwraca więc wartość NULL, a osoba dzwoniąca musi następnie sprawdzić wartość NULL lub wyrzucić NPE. Jeśli osoba dzwoniąca wie, jak poradzić sobie z sytuacją, wówczas wykonanie polecenia catch vs. try-catch może wyglądać nieco bardziej elegancko, ALE jeśli nie może sobie poradzić, musi ponownie przekazać wartość null, a osoba dzwoniąca musi ponownie sprawdzić wartość NULL itd. itd.
raudi

10
Aby być uczciwym wobec powyższego rozwiązania, naprawdę istnieją przypadki, w których konieczne jest zwrócenie wartości null zamiast zgłaszania wyjątku IllegalArgumentException i przerwania przepływu programu, na przykład odwzorowanie wyliczeń między schematem usługi sieci Web a schematem bazy danych, w którym nie zawsze są one jednym -do jednego. Zgadzam się jednak, że blok catch nigdy nie powinien pozostać pusty. Umieść kod, taki jak log.warn lub coś w celu śledzenia.
Adrian M

101

Użyj wzoru z Joshua Bloch, Effective Java :

(uproszczony dla zwięzłości)

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

Zobacz także:

Przykład Oracle Java z wykorzystaniem Enum i mapy instancji

Kolejność wykonywania bloków statycznych w typie Enum

Jak mogę wyszukać wyliczenie Java na podstawie jego wartości String


4
jeśli Joshua Bloch to powiedział, to jest to jedyna droga :-). Szkoda, że ​​zawsze muszę tutaj przewijać.
dermoritz,

11
Jest to jeszcze prostsze w Javie 8, jak możesz: Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity()))Polecam również zastąpienie metody toString () (przekazanej przez konstruktor) i użycie jej zamiast nazwy, szczególnie jeśli Enum jest powiązany z szeregowalnymi danymi, ponieważ pozwala to kontrolować obudowę bez podawania Sonar pasuje.
Novaterata

1
Java 8 z pewnością może / zmieni wiele (lepszych) odpowiedzi na tym forum. Nie jestem jednak pewien, czy kiedykolwiek ogon (sonar) macha psem (kod aplikacji).
Darrell Teague,

3
Jeśli i tak to włożysz unmodifiableMap, nie ma żadnej korzyści, aby zacząć od ConcurrentHashMap. Po prostu użyj HashMap. (Jeśli masz ImmutableMap
Guawy,

9
Inicjalizacja statyczna jest z natury zsynchronizowana , więc nie ma absolutnie żadnego powodu, aby używać jej ConcurrentHashMaptutaj, gdzie mapa nigdy nie jest modyfikowana po inicjalizacji. Dlatego nawet np. Przykład w samym JLS używa regularnego HashMap.
Radiodef,

74

Powinieneś także uważać na swoją sprawę. Pozwól, że wyjaśnię: robienie Blah.valueOf("A")prac, ale Blah.valueOf("a")nie zadziała. Potem znowu Blah.valueOf("a".toUpperCase(Locale.ENGLISH))by działało.

edytuj
Zmieniono toUpperCasena toUpperCase(Locale.ENGLISH)oparte na tc. komentarz i dokumenty java

edit2 Na Androidzie powinieneś używać Locale.US, jak wskazuje sulai .


6
Uważaj na domyślne ustawienia regionalne!
tc.

3
Jeśli chodzi o użytkowników Androida, chciałbym zauważyć, że dokumentacja Androida wyraźnie zachęca do korzystania z Locale.USwejścia / wyjścia do odczytu maszynowego.
sulai

2
@Trengot Tak, niestety. Turcja jest dobrym przykładem. W połączeniu z niedziałającą obsługą domyślnych zestawów znaków w Javie (domyślnie Latin w systemie Windows zamiast Unicode), prawie zawsze jest niebezpieczne korzystanie z domyślnych wersji metod, które akceptują zestaw znaków lub ustawienia regionalne. Prawie zawsze powinieneś je wyraźnie zdefiniować.
Stijn de Witt

Nie jestem pewien, czy „domyślne” zestawy znaków Java są same w sobie „zepsute”, ale przyznane, domyślnie UTF-8 zamiast zastąpień (które należy zawsze robić jawnie) stworzyłoby lepsze systemy dla młodszych programistów, którzy często nie rozumieją koncepcje charset.
Darrell Teague,

38

Oto metoda, która może to zrobić dla dowolnego Enum i nie uwzględnia wielkości liter.

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

Ta odmiana robi to poprawnie: equalsIgnoreCasejest to najlepsza droga. +1
Stijn de Witt

Jak rozróżnianie wielkości liter, ale ... wolę wyliczenia niż (losowe) przypisania ciągów kluczy i ... niewielkie, ale iteracja jest mniej wydajna w przypadku takiego potencjalnie powtarzalnego wyszukiwania. Stąd impl EnumMap i in.
Darrell Teague,

To nie działa ! Zmieniłem equalsIgnoreCase na równy dla mojego celu. Kod nie powiódł się, mimo że oba wejścia równe były dokładnie takie same.
MasterJoe2,

36

Używanie Blah.valueOf(string)jest najlepsze, ale możesz także korzystać Enum.valueOf(Blah.class, string).


1
Rozróżniana jest wielkość liter, nie pomaga!
Murtaza Kanchwala

@MurtazaKanchwala Czy możesz wyjaśnić swój komentarz? Co próbujesz zrobić?
Peter Lawrey

2
Cześć @PeterLawrey. Próbowałem pobrać Enum z publicznego enum String StringTyp {PERSON („Person”) public String Parametrame; ObjectType (String nazwa_parametru) {this.parameterName = nazwa parametru; } public String getParameterName () {return this.parameterName; } public static ObjectType fromString (String nazwa_parametru) {if (nazwa_parametru! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parametrName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} return null; }}
Murtaza Kanchwala

34

W Javie 8 lub nowszej przy użyciu strumieni :

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

Nie jestem pewien, czy to jakaś lepsza odpowiedź. Strumienie w tym przypadku są iteratorem jednowątkowym, jak każdy inny dla wszystkich wartości i prawdopodobnie mniej wydajnym niż implant do wyszukiwania mapy. Strumienie mają większą wartość w kontekście wielowątkowym, w którym na przykład równoległe wykonanie pliku tekstowego oddzielonego znakiem nowej linii może poprawić wydajność.
Darrell Teague,

28

Jeśli nie chcesz pisać własnego narzędzia, skorzystaj z usług Google biblioteka:

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

W przeciwieństwie do wbudowanej funkcji java pozwala sprawdzić, czy A jest obecny w Blah i nie zgłasza wyjątku.


7
smutne jest to, że zwraca Google Opcjonalne, a nie java Opcjonalne
javaProgrammer

Prawdziwe. Tak było. Google i Netflix mają świetne biblioteki Java. Tam, gdzie nakładają się na siebie klasy przechwytywania Java zaimplementowane w nowszych wersjach, nieuchronnie powoduje problemy. Coś w tym stylu musi być w jednym pakiecie dostawców.
Darrell Teague,

26

Moje 2 centy tutaj: za pomocą strumieni Java8 + sprawdzenie dokładnego ciągu:

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

** EDYTOWAĆ **

Zmieniłem nazwę tej funkcji, fromString()ponieważ od jej nazwania przy użyciu tej konwencji uzyskasz pewne korzyści z samego języka Java; na przykład:

  1. Bezpośrednia konwersja typów w adnotacji HeaderParam

1
Alternatywnie, aby umożliwić Ci pisanie bardziej czytelnych switchbloków, możesz .orElse(null)zamiast .orElseThrow()tego, aby kodować wyjątek wrzucając defaultklauzulę - i zawierać bardziej przydatne informacje, gdy jest to wymagane. Aby uczynić go bardziej łagodnym, możesz użyćv -> Objects.equals(v.name, s == null ? "" : s.trim().toUpperCase())
Adam

lub po prostu zwróć Optionalz findFirst(), pozwalając użytkownikowi zdecydować, czy chce .orElse(null), orElseThrow()czy cokolwiek innego ...
user85421,

1
Zadeklarowanie a public static MyEnum valueOf(String)jest w rzeczywistości błędem kompilacji, ponieważ jest taki sam jak domyślnie zdefiniowany, więc starsza wersja Twojej odpowiedzi jest w rzeczywistości lepsza. ( jls , ideone )
Radiodef

W mojej opcji lepiej jest unikać wyjątków i używać opcji opcjonalnej. Oprócz tego powinniśmy unieważnić wartość null i użyć również Opcjonalnego.
Hans Schreuder

Ponownie pamiętaj tylko, że nawet jeśli mniej lub lepiej wyglądający kod ... taka implementacja Stream jest po prostu iteratorem wszystkich wartości w porównaniu do wyszukiwania mapy (mniej wydajna).
Darrell Teague,

16

Może być konieczne:

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

Jeszcze jeden dodatek:

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

Spowoduje to zwrócenie Ci wartości przez Strunową Nazwę Enum. Na przykład, jeśli podasz „PERSON” w fromEnumName, zwróci Ci wartość Enum, tj. „Osoba”


13

Innym sposobem osiągnięcia tego celu jest użycie niejawnej metody statycznej name()Enum. nazwa zwróci dokładny ciąg użyty do utworzenia tego wyliczenia, którego można użyć do sprawdzenia podanego ciągu:

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

Testowanie:

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

//it will print B  B

inspiracja: 10 przykładów Enum w Javie


7
To jest w zasadzie to, co valueOfrobi dla ciebie. Ta statyczna metoda nie oferuje nic więcej, wyjątków i wszystkich innych. Zatem konstrukcje if / else są wysoce niebezpieczne ... każda nowa dodana stała wyliczania spowoduje, że ta metoda zepsuje się bez zmian.
YoYo

Rozważ też ten przykład tego, w jaki sposób możemy użyć valueOf do wyszukiwania bez rozróżniania wielkości liter, lub w jaki sposób możemy uniknąć jego wyjątku i zastosować aliasy w celu zapewnienia alternatywnych nazw: stackoverflow.com/a/12659023/744133
YoYo

2
name()nie jest statyczny.
nrubin29

10

Rozwiązanie wykorzystujące biblioteki Guava. Metoda getPlanet () nie uwzględnia wielkości liter, więc getPlanet („MerCUrY”) zwróci 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();
   }
}

8

Aby dodać do poprzednich odpowiedzi i zająć się niektórymi dyskusjami wokół wartości zerowych i NPE, korzystam z Opcji Guava do obsługi nieobecnych / nieprawidłowych przypadków. Działa to doskonale w przypadku analizowania identyfikatora URI / parametru.

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

Dla tych, którzy nie są świadomi, oto kilka informacji na temat unikania wartości null za pomocą Opcjonalnego: https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional


To jest naprawdę dobra odpowiedź na pokrewne wzorce i użycie Opcjonalnego wewnątrz Enum - wykorzystując fakt, że Enums są również klasami, a zatem można je ozdobić metodami, metodami zastępującymi itp. Jest to dobry przypadek dla programowania w stylu płynnym, ponieważ wartości zerowe zwracane z metod powodują, że konstruują one błędy (NPE w nieznanych miejscach w płynnych łańcuchach wywołań metod).
Darrell Teague,

8

W Javie 8 statyczny wzorzec mapy jest jeszcze łatwiejszy i jest moją preferowaną metodą. Jeśli chcesz używać Enum z Jacksonem, możesz zastąpić toString i użyć tego zamiast nazwy, a następnie dokonać adnotacji za pomocą@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);
    }
}

Jackson jest implementacją JSON (JavaScript Object Notation). Pierwotne pytanie nie miało nic wspólnego z JSON.
Darrell Teague,

Część JSON była w tym czasie tylko dodatkowymi rzeczami, które uważałem za istotne, ponieważ uzyskanie Enum z łańcucha jest w zasadzie rodzajem deserializacji, a JSON / Jackson jest prawdopodobnie najpopularniejszym rozwiązaniem serializacji.
Novaterata

Rozumiem, ale z punktu widzenia moderacji - nie przyczyniło się to do odpowiedzi na pytanie PO, więc po prostu próbowałem pomóc w ustaleniu kontekstu. JSON jest rzeczywiście sposobem na przekształcenie obiektów w formę kanoniczną w Javie, a Jackson jest świetną biblioteką.
Darrell Teague

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

spójrz na ten link, aby znaleźć przewodniki na temat odpowiadania i zadawania pytań na stackoverflow.com: stackoverflow.com/faq
bakoyaro,

1
To mniej więcej to samo, co odpowiedź JoséMi
Rup

6

Metoda O (1) zainspirowana kodem generowanym przez oszczędność, który wykorzystuje hashap.

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

}

Zwróć uwagę, że identyfikatory zero (0) i jeden (1) są niepotrzebne. Metoda wyliczania wartości () zwraca elementy w tej samej kolejności, w jakiej zostały zakodowane. Tak więc pierwszym wpisem będzie zero porządkowe, drugim itd.
Darrell Teague

6

Enum jest bardzo przydatne, często Enumużywałem do dodawania opisu dla niektórych pól w różnych językach, jak w poniższym przykładzie:

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

Następnie możesz dynamicznie pobrać opis w oparciu o kod języka przekazany do getDescription(String lang)metody, na przykład:

String statusDescription = Status.valueOf("ACT").getDescription("en");

1
Dobry przykład dalszego popychania Enums. Dokonałbym kodowania języka przy użyciu standardowych nazw statycznych i wyszukiwania na mapie, ale nadal ... dobry przykład posiadania wyliczenia z różnymi znacznikami dla zasadniczo tej samej wartości logicznej.
Darrell Teague

5

Co powiesz na?

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 definiuje kilka użytecznych metod, które są dostępne dla wszystkich typów wyliczeń w Javie:

  • Możesz użyć name()metody, aby uzyskać nazwę dowolnej stałej Enum. Dosłowne ciągi używane do pisania stałych enum to ich nazwy.
  • Podobnie values()można użyć metody, aby uzyskać tablicę wszystkich stałych Enum z typu Enum.
  • W przypadku zadanego pytania można użyć valueOf()metody konwersji dowolnej wartości ciągu na stałą Enum w Javie, jak pokazano poniżej.
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

W tym fragmencie kodu valueOf()metoda zwraca stałą Enum Gender.MALE, wywołującą nazwę na tej zwracanej wartości "MALE".


4

Biblioteka commons-lang w Apache ma statyczną funkcję org.apache.commons.lang3.EnumUtils.getEnum, która zamapuje łańcuch na typ Enum. Ta sama odpowiedź w zasadzie jak Geoffreys, ale po co rzucać własną, skoro już jest na wolności.


1
Uczciwy komentarz (DRY), ale ... chociaż większość rzeczy z Apache Commons jest świetna, sam znalazłem kilka błędów i anty-wzorów w tej bazie. Odnosząc się w ten sposób do stwierdzenia, że ​​wdrożenie Joshua Blocha może być silniejsze. Sprowadza się do konieczności sprawdzenia kodu Apache, aby wiedzieć, co ktoś zaimplementował. Gdyby powiedziano, że słynny Doug Leah, który ponownie napisał współbieżność Javy ... zaufałbym mu bezgranicznie.
Darrell Teague,

4

Dodanie do najwyżej ocenianej odpowiedzi z przydatnym narzędziem ...

valueOf() zgłasza dwa różne wyjątki w przypadkach, gdy nie podoba mu się jego wejście.

  • IllegalArgumentException
  • NullPointerExeption

Jeśli twoje wymagania są takie, że nie masz żadnej gwarancji, że Twój ciąg z pewnością będzie pasował do wartości wyliczeniowej, na przykład jeśli dane ciągu pochodzą z bazy danych i mogą zawierać starą wersję wyliczenia, musisz je obsłużyć często...

Oto więc napisana przeze mnie metoda wielokrotnego użytku, która pozwala nam zdefiniować domyślny Enum, który zostanie zwrócony, jeśli przekazany przez nas Ciąg nie będzie pasował.

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

Użyj tego w ten sposób:

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

Ponieważ switchjeszcze nie wspomniano o -wersji, przedstawiam ją (ponownie wykorzystując wyliczenie 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);
      }
    }
  }

Ponieważ nie daje to żadnej dodatkowej wartości valueOf(String name)metodzie, sensowne jest zdefiniowanie dodatkowej metody, jeśli chcemy mieć inne zachowanie. Jeśli nie chcemy podnosić IllegalArgumentException, możemy zmienić implementację na:

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

Poprzez dostarczanie wartości domyślnej możemy utrzymać umowę o Enum.valueOf(String name)bez rzucając IllegalArgumentException w ten sposób, że w żadnym wypadku nie nulljest zwracana. Dlatego rzucamy a NullPointerExceptionjeśli nazwa jest, nulla defaultjeśli defaultValuejest null. Tak to valueOfOrDefaultdziała.

W tym podejściu przyjęto projekt Mapinterfejsu, który zapewnia metodę Map.getOrDefault(Object key, V defaultValue)od Java 8.


1

Kolejne narzędzie przechwytujące w odwrotny sposób. Używanie wartości identyfikującej to Enum, a nie jego nazwa.

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

Przykład:

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")zwraca Foo.THREE, ponieważ użyje getValuedo dopasowania „drei”, która jest unikalną publiczną, a nie ostateczną i niestatyczną metodą w Foo. W przypadku Foo ma więcej niż o sposobie publicznego, a nie ostatecznego, a nie statycznym, na przykład, getTranslatektóry powraca „drei”, druga metoda może być stosowana: EnumUtil.from(Foo.class, "drei", "getTranslate").


1

Rozwiązanie Kotlin

Utwórz numer wewnętrzny, a następnie zadzwoń valueOf<MyEnum>("value"). Jeśli typ jest nieprawidłowy, otrzymasz null i będziesz musiał go obsłużyć

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

Alternatywnie możesz ustawić wartość domyślną, wywołując valueOf<MyEnum>("value", MyEnum.FALLBACK)i unikając odpowiedzi zerowej. Możesz rozszerzyć swoje wyliczenie, aby domyślnie było automatyczne

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

Lub jeśli chcesz jedno i drugie, zrób drugie:

inline fun <reified T : Enum<T>> valueOf(type: String, default: T): T = valueOf<T>(type) ?: default

Czy uważasz, że Twoja odpowiedź będzie miała lepszy dom? stackoverflow.com/questions/28548015/…
nabster

0

Lubię używać tego rodzaju procesu do analizowania poleceń jako ciągów znaków w wyliczeniach. Zwykle mam jedno z wyliczeń jako „nieznane”, więc warto zwrócić je, gdy nie zostaną znalezione inne (nawet bez rozróżniania wielkości liter), a nie puste (to znaczy, że nie ma wartości). Dlatego używam tego podejścia.

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

Najszybszym sposobem na uzyskanie nazwy wyliczenia jest utworzenie mapy tekstu i wartości wyliczenia podczas uruchamiania aplikacji oraz uzyskanie nazwy wywołanie funkcji 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());
    } 
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.