Używanie wartości Enum jako literałów łańcuchowych


389

Jaki jest najlepszy sposób na użycie wartości przechowywanych w Enum jako literałów łańcuchowych? Na przykład:

public enum Modes {
    some-really-long-string,
    mode1,
    mode2,
    mode3
}

Później mógłbym użyć Mode.mode1do zwrócenia jego reprezentacji ciągu jako mode1. Bez konieczności dzwonienia Mode.mode1.toString().

Odpowiedzi:


646

Nie możesz Myślę, że masz tutaj CZTERY opcje. Wszystkie cztery oferują rozwiązanie, ale z nieco innym podejściem ...

Opcja pierwsza: użyj wbudowanego name()wyliczenia. Jest to całkowicie w porządku, jeśli nie potrzebujesz żadnego specjalnego formatu nazewnictwa.

    String name = Modes.mode1.name(); // Returns the name of this enum constant, exactly as declared in its enum declaration.

Opcja druga: dodaj nadpisujące właściwości do swoich wyliczeń, jeśli chcesz mieć większą kontrolę

public enum Modes {
    mode1 ("Fancy Mode 1"),
    mode2 ("Fancy Mode 2"),
    mode3 ("Fancy Mode 3");

    private final String name;       

    private Modes(String s) {
        name = s;
    }

    public boolean equalsName(String otherName) {
        // (otherName == null) check is not needed because name.equals(null) returns false 
        return name.equals(otherName);
    }

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

Opcja trzecia: użyj statycznych finałów zamiast wyliczeń:

public final class Modes {

    public static final String MODE_1 = "Fancy Mode 1";
    public static final String MODE_2 = "Fancy Mode 2";
    public static final String MODE_3 = "Fancy Mode 3";

    private Modes() { }
}

Opcja czwarta: interfejsy mają wszystkie pola publiczne, statyczne i końcowe:

public interface Modes {

    String MODE_1 = "Fancy Mode 1";
    String MODE_2 = "Fancy Mode 2";
    String MODE_3 = "Fancy Mode 3";  
}

46
Ta odpowiedź jest w rzeczywistości błędna: jak można zadzwonić .name()Patrz: stackoverflow.com/a/6667365/887836
Alex

3
@ kato2 niepoprawne. Metoda .name () jest tworzona automatycznie przez kompilator
Sean Patrick Floyd

10
JavaDoc: String java.lang.Enum.name () Zwraca nazwę tej stałej wyliczeniowej, dokładnie tak jak zadeklarowano w jej deklaracji wyliczeniowej. Większość programistów powinna używać metody toString zamiast tej, ponieważ metoda toString może zwrócić bardziej przyjazną dla użytkownika nazwę. Ta metoda jest przeznaczona przede wszystkim do stosowania w wyspecjalizowanych sytuacjach, w których poprawność zależy od uzyskania dokładnej nazwy, która nie będzie się różnić w zależności od wydania. Zwraca: nazwa tej stałej wyliczeniowej
SuperRetro

Bardziej @Ryan Stewart wymaga mniej kodu i mniej kodu == mniejsze prawdopodobieństwo błędów
Eric

4
Nie używaj interfejsów do przechowywania stałych: Skuteczna Java (wydanie trzecie) zaleca „używanie interfejsów tylko do definiowania typów” (pozycja 22).
Olivier Grégoire

481

Każde wyliczenie ma zarówno metodę name (), jak i valueOf (String). Pierwszy zwraca nazwę wyliczenia, a drugi podaje wartość wyliczenia, której nazwą jest ciąg. Czy to jest to, czego szukasz?

String name = Modes.mode1.name();
Modes mode = Modes.valueOf(name);

Istnieje również wartość statycznaOf (klasa, łańcuch) na samym Enum, więc możesz również użyć

Modes mode = Enum.valueOf(Modes.class, name);

50
TO powinna być ODPOWIEDŹ! Używanie czegoś takiego jak A („A”) może być źródłem błędów i jest to bezsensowna dodatkowa praca!
Firzen

12
@Firzen nie, jeśli wartość ciągu może zawierać spacje lub łączniki, jak w przypadku some-really-long-string.
ceving

@ceving Pytanie nie jest dobrze sformułowane w odniesieniu do spacji i łączników. Pytania pokazują zdziwiony przykład, ale nie pytają, jak utworzyć Enum przy użyciu dzielonych wartości. Zamiast tego pytanie pyta, jak uzyskać wartość ciągu bez konieczności wywoływania metody toString bez określania dzielonych wartości są wymagane. To powiedziawszy, myślę, że może to być lepsza odpowiedź, jeśli zostanie zmieniona, aby wspomnieć, że wartości Enum muszą nadal przestrzegać reguł nazewnictwa Java i będą musiały użyć czegoś wymienionego w akceptowanej odpowiedzi, jeśli takie znaki będą wymagane.
Hazok

Chciałem dodać link do mkyong, który używa tej valueOf(String)metody w połączeniu z, toUpperCase(Locale)aby zapewnić konwersję String.
Nieznany Id

2
Powodem, dla którego wiele osób woli podejście własności od Enum.name (), jest to, że logika oparta na Enum.name () jest wtedy na zawsze na łasce nazw wartości. Jeśli kod zmienia się w przyszłości, może to zmienić się w trywialny problem, ponieważ cała poprzednia logika psuje się przy zmianach zestawu wartości Enum. Przesłonięcie i użycie metody toString () pozwala deweloperowi na większą kontrolę nad używanymi wartościami, w tym powielaniem, niepoprawnymi znakami nazw zmiennych itp. Nie zapomnij również o zastąpieniu valueOf ().
niepodzielny

79

Możesz zastąpić toString()metodę dla każdej wartości wyliczenia.

Przykład:

public enum Country {

  DE {
    @Override
    public String toString() {
      return "Germany";
    }
  },
  IT {
    @Override
    public String toString() {
      return "Italy";
    }
  },
  US {
    @Override
    public String toString() {
      return "United States";
    }
  }

}

Stosowanie:

public static void main(String[] args) {
  System.out.println(Country.DE); // Germany
  System.out.println(Country.IT); // Italy
  System.out.println(Country.US); // United States
}

2
Lubię to. Nie ma powodu, aby nie używać wyliczenia jako klasy z dalszymi funkcjami, takimi jak uzyskanie listy wszystkich wartości, uzyskanie ciągu każdego typu itp.
diegosasw

8
Brzydkie i nie do wielokrotnego użytku. Znacznie lepiej jest podać wartość ciągu jako konstruktora dla Country, a następnie zastąpić metodę toString () dla wyliczenia.
greg7gkb

4
Jest to dobra technika, gdy masz dość duże wyliczenie i chcesz tylko zastąpić to, co drukuje dla jednego lub dwóch członków.
Donal Fellows,

3
To wcale nie jest skalowane. Nie rób tego
sebnukem

1
Ma to dla mnie sens
hanzolo,

35

Jak wspomina Benny Neugebauer, możesz zastąpić toString (). Zamiast tego nadpisując toString dla każdego pola wyliczeniowego, bardziej podoba mi się coś takiego:

public enum Country{
    SPAIN("España"),
    ITALY("Italia"),
    PORTUGAL("Portugal");


    private String value;

    Country(final String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return this.getValue();
    }
}

Możesz także dodać metodę statyczną, aby pobrać wszystkie pola, wydrukować je wszystkie itp. Wystarczy wywołać getValue, aby uzyskać ciąg powiązany z każdym elementem Enum



21
public enum Modes {
  MODE1("Mode1"),
  MODE2("Mode2"),
  MODE3("Mode3");

 private String value;
 public String getValue() {
    return value;
   }
 private Modes(String value) {
  this.value = value;
 } 
}

możesz wykonać połączenie jak poniżej, gdziekolwiek chcesz uzyskać wartość jako ciąg z wyliczenia.

Modes.MODE1.getvalue();

Zwróci „Mode1” jako ciąg znaków.


6

Możesz używać, Mode.mode1.name()jednak często nie musisz tego robić.

Mode mode =
System.out.println("The mode is "+mode);

6
Warto zauważyć, że operator + wywoła metodę toString () na wyliczeniu, a nie name (). I toString () może zostać zastąpione, aby powrócić coś innego niż nazwa (nawet jeśli nie jest pożądane)
JB Nizet

1
Oba name()i toString()mogą zostać zastąpione, ale mam nadzieję, że będzie to jasne po przeczytaniu kodu, enumjeśli tak się dzieje.
Peter Lawrey,

9
Nie. Name () jest ostateczny i zawsze zwraca nazwę wyliczenia zadeklarowaną w deklaracji wyliczenia.
JB Nizet

1
@JB Nizet, masz rację. name()jest final. Dziękuję za poprawienie mnie. :)
Peter Lawrey

6

O ile mi wiadomo, jedynym sposobem na uzyskanie nazwy byłoby

Mode.mode1.name();

Jeśli naprawdę potrzebujesz tego w ten sposób, możesz:

public enum Modes {
    mode1 ("Mode1"),
    mode2 ("Mode2"),
    mode3 ("Mode3");

    private String name;       

    private Modes(String s) {
        name = s;
    }
}

1
Ale w tym przypadku Mode.mode1nadal nie jest typu String.
Larry,

No tak. Potrzebna byłaby metoda getName (), która pokonuje cel, więc nie, nie można tego zrobić.
Jake Roussel,

„nazwa” to zła nazwa pola, jest to standardowe pole wyliczenia.
Wooff,

6

Jeśli chodzi o moje wyliczenia, nie bardzo lubię myśleć, że przydzielono im po 1 Ciąg. W ten sposób implementuję metodę toString () na wyliczeniach.

enum Animal
{
    DOG, CAT, BIRD;
    public String toString(){
        switch (this) {
            case DOG: return "Dog";
            case CAT: return "Cat";
            case BIRD: return "Bird";
        }
        return null;
    }
}

1
Zgłoszenie wyjątku czasu wykonywania byłoby lepsze niż zwracanie wartości null, ponieważ powinien to być nieosiągalny kod?
wynajmuje

Jest returnto zbędne, ponieważ włączanie wyliczeń wszystkich wyliczeń kończy się.
Danon

5

Możesz po prostu użyć:

""+ Modes.mode1

Nie jestem tego w 100% pewien, ale o ile wiem, ta obsada nie jest konieczna, prawda? Łączenie pustego ciągu z inną zmienną powinno automatycznie wywoływać konwersję, czy są jakieś wyjątki od tej reguły?
Nieznany Id

1
Masz rację, poprawna wersja powinna być "" + Modes.mode1. Naprawiłem odpowiedź
Buddy,

Odpowiedź EB jest również korzystna, ponieważ przypomina idiom python ''.join()
antropiczny android

5

moje rozwiązanie twojego problemu!

import java.util.HashMap;
import java.util.Map;

public enum MapEnumSample {
    Mustang("One of the fastest cars in the world!"), 
    Mercedes("One of the most beautiful cars in the world!"), 
    Ferrari("Ferrari or Mercedes, which one is the best?");

    private final String description;
    private static Map<String, String> enumMap;

    private MapEnumSample(String description) {
        this.description = description;
    }

    public String getEnumValue() {
        return description;
    }

    public static String getEnumKey(String name) {
        if (enumMap == null) {
            initializeMap();
        }
        return enumMap.get(name);
    }

    private static Map<String, String> initializeMap() {
        enumMap = new HashMap<String, String>();
        for (MapEnumSample access : MapEnumSample.values()) {
            enumMap.put(access.getEnumValue(), access.toString());
        }
        return enumMap;
    }

    public static void main(String[] args) {

        // getting value from Description
        System.out.println(MapEnumSample.getEnumKey("One of the fastest cars in the world!"));

        // getting value from Constant
        System.out.println(MapEnumSample.Mustang.getEnumValue());

        System.out.println(MapEnumSample.getEnumKey("One of the most beautiful cars in the world!"));
        System.out.println(MapEnumSample.Mercedes.getEnumValue());

        // doesnt exist in Enum
        System.out.println("Mustang or Mercedes, which one is the best?");
        System.out.println(MapEnumSample.getEnumKey("Mustang or Mercedes, which one is the best?") == null ? "I don't know!" : "I believe that "
                + MapEnumSample.getEnumKey("Ferrari or Mustang, which one is the best?") + " is the best!.");

        // exists in Enum
        System.out.println("Ferrari or Mercedes, wich one is the best?");
        System.out.println(MapEnumSample.getEnumKey("Ferrari or Mercedes, which one is the best?") == null ? "I don't know!" : "I believe that "
                + MapEnumSample.getEnumKey("Ferrari or Mercedes, which one is the best?") + " is the best!");

    }
}

Wygląda na to, że problem OP został rozwiązany 4 lata temu, ale witamy w StackOverflow :)
TDG

1
Dzięki, że pomogłem komuś takiemu jak ja, szukając bardziej obiektywnych odpowiedzi na stare problemy :)
Renan Galdino da Silva

3

Enum to tylko trochę wyjątkowa klasa. Wyliczenia mogą przechowywać dodatkowe pola, wdrażać metody itp. Na przykład

public enum Modes {
    mode1('a'),
    mode2('b'),
    mode3('c'),
    ;
    char c;

    private Modes(char c) {
        this.c = c;
    }
    public char character() {
        return c;
    }
}

Teraz możesz powiedzieć:

System.out.println(Modes.mode1.character())

i zobacz wyjście: a


3
package com.common.test;

public  enum Days {


    monday(1,"Monday"),tuesday(2,"Tuesday"),wednesday(3,"Wednesday"),
    thrusday(4,"Thrusday"),friday(5,"Friday"),saturday(6,"Saturday"),sunday(7,"Sunday");

    private int id;
    private String desc;


    Days(int id,String desc){
        this.id=id;
        this.desc=desc;
    }

    public static String getDay(int id){

        for (Days day : Days.values()) {
            if (day.getId() == id) {
                return day.getDesc();
            }
        }
        return null;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }



};

1
Czy możesz wyjaśnić, jak to rozwiązałoby problem?
Phani

możesz wywołać to wyliczenie gdziekolwiek, używając tej linii: int id = 1; Ciąg dayName = Days.getDay (id); , podaj tutaj id. zwróci opis tego identyfikatora, czyli „wtorek”

2

Ta metoda powinna działać z każdym enum:

public enum MyEnum {
    VALUE1,
    VALUE2,
    VALUE3;

    public int getValue() {
        return this.ordinal();
    }

    public static DataType forValue(int value) {
        return values()[value];
    }

    public String toString() {
        return forValue(getValue()).name();
    }
}

2
public enum Environment
{
    PROD("https://prod.domain.com:1088/"),
    SIT("https://sit.domain.com:2019/"),
    CIT("https://cit.domain.com:8080/"),
    DEV("https://dev.domain.com:21323/");

    private String url;

    Environment(String envUrl) {
        this.url = envUrl;
    }

    public String getUrl() {
        return url;
    }
}

String prodUrl = Environment.PROD.getUrl();

Wydrukuje:

https://prod.domain.com:1088/

Ten projekt dla stałych ciągu wyliczeniowego działa w większości przypadków.


0

po wielu próbach przyszło mi to rozwiązanie

public static enum Operation {

    Addition, Subtraction, Multiplication, Division,;

    public String getUserFriendlyString() {
        if (this==Addition) {
            return " + ";
        } else if (this==Subtraction) {
            return " - ";
        } else if (this==Multiplication) {
            return " * ";
        } else if (this==Division) {
            return " / ";
        }
        return "undefined";
       }
}

0

Możesz spróbować:

public enum Modes {
    some-really-long-string,
    mode1,
    mode2,
    mode3;

    public String toString(){
        switch(this) {
            case some-really-long-string:
                return "some-really-long-string";
            case mode2:
                return "mode2";
            default: return "undefined";
        }
    }

}


0

Odkryłem, że ten jest łatwiejszy do uniknięcia błędu typu:

public enum Modes {
    some-really-long-string,
    mode1,
    mode2,
    mode3;

    String str;

    Modes(){
        this.str = super.name();
    }

    @Override
    @NonNull
    public String toString() {
        return str;
    }

jednak - może to działać, gdy trzeba użyć ciągu znaków w dzienniku / println lub za każdym razem, gdy Java kompiluje metodę toString () automatycznie, ale w wierszu kodu takim jak ten ->

// sample method that require (string,value)
intent.putExtra(Modes.mode1 ,shareElement.getMode()); // java error
// first argument enum does not return value

zamiast tego, jak wspomniano powyżej, nadal będziesz musiał rozszerzyć wyliczenie i użyć .name() w takich przypadkach:

intent.putExtra(Modes.mode1.name() ,shareElement.getMode()); 
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.