Використання значень Enum в якості лінійних рядків


389

Який найкращий спосіб використовувати значення, збережені в Enum, як рядкові літерали? Наприклад:

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

Тоді пізніше я міг би використати Mode.mode1для повернення його рядкове представлення як mode1. Без необхідності тримати дзвінки Mode.mode1.toString().

Відповіді:


646

Ви не можете. Я думаю, у вас тут є ЧОТИРИ варіанти. Усі чотири пропонують рішення, але з дещо іншим підходом ...

Варіант перший: використовувати вбудований 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 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 () Повертає ім'я цієї константи перерахунку, точно так, як це заявлено в її декларації enum. Більшість програмістів повинні використовувати метод toString віддати перевагу цьому, оскільки метод toString може повернути більш зручне ім'я. Цей метод розроблений в основному для використання в спеціалізованих ситуаціях, коли від правильності залежить отримання точного імені, яке не буде змінюватися від випуску до випуску. Повертається: назва цієї константи перерахунку
SuperRetro

Пристрастіший @Ryan Stewart вимагає менше коду та менше коду == менше шансів на помилки
Ерік

4
Не використовуйте інтерфейси для зберігання констант: Ефективна Java (3-е видання) рекомендує "використовувати інтерфейси лише для визначення типів" (пункт 22).
Олів'є Грегоар

481

Кожен перерахунок має як ім'я (), так і значення valueOf (String). Перший повертає ім'я рядка enum, а другий дає значення enum, ім'я якого - рядок. Це схоже на те, що ви шукаєте?

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

Також у самому Enum є статичне valueOf (Class, String), тому ви також можете використовувати

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

50
Це повинно бути ВІДПОВІДЬ! Використання чогось типу A ("A") може стати джерелом помилок, і безглузда додаткова робота!
Фірцен

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()метод для кожного значення enum.

Приклад:

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

3
Це зовсім не масштабується. Не робіть цього.
sebnukem

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

ви можете здійснити дзвінок, як нижче, де б ви не хотіли отримати значення у вигляді рядка від enum.

Modes.MODE1.getvalue();

Це поверне "Mode1" у вигляді рядка.


6

Ви можете використовувати, Mode.mode1.name()однак вам часто не потрібно цього робити.

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

6
Варто зазначити, що оператор + зателефонує toString () на enum, а не name (). І toString () може бути відмінено, щоб повернути щось інше, ніж ім’я (навіть якщо це не бажано)
JB Nizet

1
І те name()й toString()інше може бути відмінено, але, сподіваємось, це буде зрозуміло з читання коду, enumякщо це відбувається.
Пітер Лорі

9
Ім'я № () є остаточним, і завжди повертає ім'я enum, як заявлено в його декларації enum.
JB Nizet

1
@JB Nizet, ти маєш рацію. 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

Енум - просто трохи особливий клас. Енуми можуть зберігати додаткові поля, застосовувати методи тощо. Наприклад

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
Чи можете ви пояснити, як би вирішити проблему?
Фані

ви можете зателефонувати цьому enum будь-де, використовуючи цей рядок: 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/

Ця конструкція для контурних констант enum працює в більшості випадків.


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 для журналу / 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.