Использование значений Enum в качестве строковых литералов


389

Каков наилучший способ использовать значения, хранящиеся в Enum, как строковые литералы? Например:

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

Затем позже я мог бы использовать Mode.mode1для возвращения его строковое представление как mode1. Без необходимости продолжать звонить Mode.mode1.toString().

Ответы:


648

Ты не можешь Я думаю, что у вас есть четыре варианта здесь. Все четыре предлагают решение, но с немного другим подходом ...

Вариант первый: использовать встроенное в name()enum. Это прекрасно, если вам не нужен специальный формат имен.

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

Вариант второй: добавьте переопределенные свойства в свои перечисления, если вы хотите больше контроля

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

Вариант третий: использовать статические финалы вместо перечислений:

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

Вариант четвертый: интерфейсы имеют каждое поле public, static и final:

public interface Modes {

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

46
Этот ответ на самом деле неверен: как вы можете позвонить .name()См .: stackoverflow.com/a/6667365/887836
Алекс

3
@ kato2 не правильно. Метод .name () создается автоматически компилятором
Шон Патрик Флойд

10
JavaDoc: String java.lang.Enum.name () Возвращает имя этой константы перечисления, точно так, как объявлено в объявлении ее перечисления. Большинство программистов должны использовать метод toString вместо этого, так как метод toString может возвращать более удобное для пользователя имя. Этот метод предназначен в первую очередь для использования в специализированных ситуациях, где правильность зависит от получения точного имени, которое не будет меняться от выпуска к выпуску. Возвращает: имя этой константы перечисления
SuperRetro

Лучше @Ryan Stewart требуется меньше кода и меньше кода == меньше шансов на ошибки
Эрик

4
Не используйте интерфейсы для хранения констант: Effective Java (3-е издание) рекомендует «использовать интерфейсы только для определения типов» (пункт 22).
Оливье Грегуар

481

Каждое перечисление имеет метод name () и метод valueOf (String). Первый возвращает строковое имя перечисления, а второй дает значение перечисления, именем которого является строка. Это то, что вы ищете?

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

В самом Enum есть также статическое значениеOf (Class, String), так что вы также можете использовать

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

50
ЭТО должно быть ОТВЕТ! Использование чего-то вроде A («A») может стать источником ошибок, и это лишняя работа!
Firzen

12
@Firzen нет, если строковое значение может содержать пробелы или дефисы, как в случае с some-really-long-string.
выступление

@ceving Вопрос плохо сформулирован в отношении пробелов и дефисов. В вопросах показан пример с переносами, но не задается вопрос, как создать Enum с использованием значений с переносами. Вместо этого вопрос состоит в том, как получить значение String без необходимости вызова toString без указания дефисных значений. Тем не менее, я думаю, что это могло бы быть лучшим ответом, если бы в него были внесены поправки, чтобы упомянуть, что значения Enum должны все еще следовать правилам именования Java и должны будут использовать что-то, упомянутое в принятом ответе, если такие символы требуются.
Хазок

Я хотел добавить ссылку на mkyong, который использует valueOf(String)метод в сочетании с toUpperCase(Locale)для обеспечения преобразования строк.
Неизвестный Id

2
Причина, по которой многие люди предпочитают подход с использованием свойств по сравнению с Enum.name (), заключается в том, что логика, основанная на Enum.name (), навсегда зависит от имен значений. Если код будет каждый раз меняться в будущем, это может превратиться в нетривиальную проблему для обхода, так как вся предыдущая логика сломается при изменении набора значений Enum. Переопределение и использование подхода toString () позволяет разработчику иметь больший контроль над используемыми значениями, включая дублирование, недопустимые символы имени переменной и т. Д. Просто не забудьте также переопределить valueOf ().
неделимый

79

Вы можете переопределить toString()метод для каждого значения перечисления.

Пример:

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

}

Применение:

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
Мне это нравится. Нет причин не использовать enum как класс с дополнительными функциями, такими как получение списка всех значений, получение строки каждого типа и т. Д.
diegosasw

8
Гадкий и не пригодный для повторного использования. Гораздо лучше предоставить строковое значение в качестве конструктора для Country, а затем переопределить метод toString () для enum.
greg7gkb

4
Это хороший метод, когда у вас достаточно большое перечисление и вы хотите переопределить только то, что печатают, как для одного или двух членов.
Donal Fellows

3
Это не масштабируется вообще. Не делай этого.
себукем

1
Это имеет смысл для меня
Hanzolo

35

Как упоминает Бенни Нойгебауэр, вы можете перезаписать toString (). Однако вместо того, чтобы перезаписывать toString для каждого поля перечисления, мне нравится что-то вроде этого:

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

Вы также можете добавить статический метод для извлечения всех полей, их печати и т. Д. Просто вызовите getValue, чтобы получить строку, связанную с каждым элементом 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;
 } 
}

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

Modes.MODE1.getvalue();

Это вернет «Mode1» в виде строки.


6

Вы можете использовать, Mode.mode1.name()однако вам часто не нужно это делать.

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

6
Стоит отметить, что оператор + будет вызывать toString () для перечисления, а не name (). И toString () может быть переопределено, чтобы возвращать что-то отличное от имени (даже если это нежелательно)
JB Nizet

1
И то, name()и другое toString()можно переопределить, но, надеюсь, это станет ясно из чтения кода, enumесли это происходит.
Питер Лоури

9
Name () является окончательным и всегда возвращает имя перечисления, как объявлено в объявлении перечисления.
JB Низет

1
@JB Низет, Вы правы. name()есть final. Спасибо, что поправили меня. :)
Питер Лори

6

Насколько я знаю, единственный способ получить имя будет

Mode.mode1.name();

Однако если вам это действительно нужно, вы можете сделать:

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

    private String name;       

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

1
Но в этом случае Mode.mode1все еще не тип String.
Ларри

О верно. Вам понадобится метод getName (), который побеждает цель, поэтому нет, вы не можете сделать это.
Джейк Руссел

"name" - это неправильное имя поля, это стандартное поле enum.
Wooff

6

Для моих перечислений мне не очень нравится думать о том, что им присваивается по 1 строке. Вот как я реализую метод toString () для перечислений.

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
Бросать исключение во время выполнения было бы лучше, чем возвращать ноль, так как это должен быть недоступный код?
арендует

return избыточно, потому что включение перечислений со всеми перечислениями завершается.
Данон

5

Вы можете просто использовать:

""+ Modes.mode1

Я не уверен на 100% в этом, но насколько мне известно, этот состав не нужен, не так ли? Объединение пустой строки с другой переменной должно автоматически вызывать преобразование, или есть какие-то исключения из этого правила?
Неизвестный Id

1
Вы правы, правильная версия должна быть "" + Modes.mode1. Я исправил ответ
приятель

Ответ EB также выигрывает от того, что он напоминает идиому питона ''.join()
антропный андроид

5

мое решение для вашей проблемы!

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

    }
}

Похоже, что проблема ОП была решена 4 года назад, но добро пожаловать в StackOverflow :)
TDG

1
Спасибо, просто чтобы помочь кому-то, как я, в поисках более объективных ответов на старые проблемы :)
Ренан Галдино да Силва

3

Enum - это немного особенный класс. Перечисления могут хранить дополнительные поля, реализовывать методы и т. Д. Например

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

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

Теперь вы можете сказать:

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

и увидеть вывод: 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
Можете ли вы объяснить, как это решит проблему?
Фани

Вы можете вызвать это перечисление в любом месте, используя эту строку: int id = 1; String dayName = Days.getDay (id); Передайте здесь идентификатор. он вернет описание для того идентификатора, который называется "вторник"

2

Этот метод должен работать с любым 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();

Он напечатает:

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

Этот дизайн для перечисляемых строковых констант работает в большинстве случаев.


0

после многих попыток я пришел с этим решением

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

Вы можете попробовать это:

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

я обнаружил, что это легче предотвратить ошибку типа:

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

    String str;

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

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

однако - это может работать, когда вам нужно использовать String в log / println или всякий раз, когда java компилирует метод toString () автоматически, но в строке кода, подобной этой ->

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

вместо этого, как упомянуто выше, вам все равно придется расширять перечисление и использовать .name() в таких случаях, как это:

intent.putExtra(Modes.mode1.name() ,shareElement.getMode()); 
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.