Замініть значенняof () та toString () в Java enum


122

Значення в моєму - enumце слова, які повинні мати пробіли в них, але перерахунки не можуть мати пробілів у своїх значеннях, тому все це згуртовано. Я хочу замінити, toString()щоб додати ці пробіли там, де я їм це скажу.

Я також хочу, щоб enum мав правильний перелік, коли я використовую valueOf()ту саму рядок, до якого я додав пробіли.

Наприклад:

public enum RandomEnum
{
     StartHere,
     StopHere
}

Виклик toString()на RandomEnumзначення якого StartHereповертається рядок "Start Here". Виклик valueof()цієї ж строки ( "Start Here") повертає значення перерахунку StartHere.

Як я можу це зробити?


2
Додайте тег "Java", щоб отримати більше коментарів / відповідей.
— Java42

Відповіді:


197

Ви можете спробувати цей код. Оскільки ви не можете замінити valueOfметод, вам слід визначити спеціальний метод ( getEnumу прикладі коду нижче), який повертає потрібне вам значення та змінить клієнта на використання цього методу.

public enum RandomEnum {

    StartHere("Start Here"),
    StopHere("Stop Here");

    private String value;

    RandomEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

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

    public static RandomEnum getEnum(String value) {
        for(RandomEnum v : values())
            if(v.getValue().equalsIgnoreCase(value)) return v;
        throw new IllegalArgumentException();
    }
}

4
getEnum можна скоротити, якщо зробити наступне: v.getValue (). дорівнює (значення); нульова перевірка може бути опущена.
— rtcarlson

Ви також можете ліниво скласти карту і використовувати її повторно, але остерігайтеся проблем з нитками під час її створення.
— Maarten Bodewes

11
не працює в сценаріях, коли ви не можете змінити виклик методу valueOf () на getValue (), наприклад, коли ви визначаєте певні поля за допомогою анотацій на
— сплячку,

Поки Enums не зафіксований на Java, @Bat має більш відповідне і OO дружнє рішення. name () не повинно бути остаточним.
— Ендрю Т Фіннелл

замість для вас можна використовувати valueOf (RandomEnum.class, значення);
— Войтек

22

Спробуйте це, але я не впевнений, що буде працювати десь :)

public enum MyEnum {
    A("Start There"),
    B("Start Here");

    MyEnum(String name) {
        try {
            Field fieldName = getClass().getSuperclass().getDeclaredField("name");
            fieldName.setAccessible(true);
            fieldName.set(this, name);
            fieldName.setAccessible(false);
        } catch (Exception e) {}
    }
}

18
Чому я єдиний, кому такі речі виглядають злі? Я маю на увазі, що це виглядає дуже здорово, але хтось без контексту, хто читає цей код, швидше за все, буде схожий на "WTF?".
— Ніколя Гійом

11
@NicolasGuillaume Це такий код, який, якщо він буде реалізований, відчайдушно потребуватиме коментар, в якому пояснюється, чому він існує і яку проблему намагався вирішити програміст. :-)
— Ti Strga


2
Це жахлива ідея. Не останньою з них є можливість безшумної помилки без вказівки чи відновлення. Також тепер значення "name" та символічне значення в коді (наприклад, A, B) різні. +2 за те, що спритний. -200 за те, що жахливо розумний. Ні в якому разі я б не схвалив цю перевірку коду.
— pedorro

1
@pedorro Це здається не таким страшним, як ви це робите. Той, хто розумів, що все, що робить Комітет Java, не слід сприймати як Євангеліє, передає це в Огляді Кодексу. Насправді гірше переписувати, а потім підтримувати всі методи утиліти Enum, тому що ім'я () неможливо змінити. Замість того, щоб зловити виняток і проковтнути його, слід кинути RuntimeException, і тоді це добре.
— Ендрю Т Фіннелл

14

Ви можете використовувати статичну карту в перерахунку, яка відображає рядки для констант перерахунку. Використовуйте його в статичному методі "getEnum". Це пропускає необхідність ітерації через перерахунки щоразу, коли ви хочете отримати одне зі свого значення String.

public enum RandomEnum {

    StartHere("Start Here"),
    StopHere("Stop Here");

    private final String strVal;
    private RandomEnum(String strVal) {
        this.strVal = strVal;
    }

    public static RandomEnum getEnum(String strVal) {
        if(!strValMap.containsKey(strVal)) {
            throw new IllegalArgumentException("Unknown String Value: " + strVal);
        }
        return strValMap.get(strVal);
    }

    private static final Map<String, RandomEnum> strValMap;
    static {
        final Map<String, RandomEnum> tmpMap = Maps.newHashMap();
        for(final RandomEnum en : RandomEnum.values()) {
            tmpMap.put(en.strVal, en);
        }
        strValMap = ImmutableMap.copyOf(tmpMap);
    }

    @Override
    public String toString() {
        return strVal;
    }
}

Просто переконайтеся, що статична ініціалізація карти відбувається нижче декларації констант enum.

BTW - тип "ImmutableMap" походить від API guava Google, і я, безумовно, рекомендую його у таких випадках.


EDIT - За коментарями:

  1. Це рішення передбачає, що кожне призначене значення рядка є унікальним і недійсним. Зважаючи на те, що творець enum може керувати цим і що рядок відповідає унікальному & ненульовому значенню enum, це здається безпечним обмеженням.
  2. Я додав метод 'toSTring ()', як про це було задано у запитанні

1
Я отримав пару голосів за цю відповідь - когось не хвилює, чому ця відповідь погана? Мені просто цікаво, що думають люди.
— педроро

Це не вдасться до сценарію, що має кілька констант перерахунку з таким самим "значенням", як RestartHere("replay"), ReplayHere("replay")або взагалі немає "значення" PauseHere, WaitHere(тобто, enum має конструктор за замовчуванням щось на кшталтprivate RandomEnum() { this.strVal = null; }
— sactiw

1
Ви повинні використовувати ImmutableMap.Builder замість створення MutableMap для створення + ImmutableMap :: copyOf.
— Брайан Коррелл

Це виглядає цікаво, але якщо перерахунок має лише кілька різних значень, то перебирати їх не буде потрібно багато часу. Напевно, варто лише того, якщо переліки мають багато цінностей.
— Бен Терлі

13

Як щодо реалізації Java 8? (null може бути замінено вашим Enum за замовчуванням)

public static RandomEnum getEnum(String value) {
    List<RandomEnum> list = Arrays.asList(RandomEnum.values());
    return list.stream().filter(m -> m.value.equals(value)).findAny().orElse(null);
}

Або ви можете використовувати:

...findAny().orElseThrow(NotFoundException::new);

2

Я не думаю, що ви збираєтеся змусити valueOf ("Почати тут") працювати. Але що стосується пробілів ... спробуйте наступне ...

static private enum RandomEnum {
    R("Start There"), 
    G("Start Here"); 
    String value;
    RandomEnum(String s) {
        value = s;
    }
}

System.out.println(RandomEnum.G.value);
System.out.println(RandomEnum.valueOf("G").value);

Start Here
Start Here

2

Нижче наведена приємна загальна альтернатива valueOf ()

public static RandomEnum getEnum(String value) {
  for (RandomEnum re : RandomEnum.values()) {
    if (re.description.compareTo(value) == 0) {
      return re;
    }
  }
  throw new IllegalArgumentException("Invalid RandomEnum value: " + value);
}

це використання compareTo()більш елегантне, ніж equals()для струнних?
— Бетліста

Елегантність - це не проблема - я погоджуюся, що це .equals()буде добре. Можна використовувати, ==якщо хочете. (Хоча вагомих причин цього не робити, якщо ви коли-небудь замінюєте перерахунок класом.)
— виняток

3
@exception НІКОЛИ ВИКОРИСТАННЯ == для String зіставлень, оскільки це зробить перевірку рівності об'єктів, тоді як ви, що вам потрібно, це перевірка рівності вмісту і для цього використовуйте метод equals () класу String.
— sactiw

2

Ви все ще маєте можливість реалізувати у своєму переліку:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name){...}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.