Замініть значення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.