Переопределить valueof () и toString () в перечислении Java


122

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

Я также хочу, чтобы перечисление предоставляло правильное перечисление, когда я использую 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 (). equals (value); нулевую проверку можно опустить.
— rtcarlson

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

11
не работает в сценариях, где вы не можете изменить вызов метода valueOf () на getValue (), например, когда вы определяете определенные поля с помощью аннотаций Hibernate для сопоставления со значениями перечисления
— mmierins

Пока Enums не будет исправлено в Java, у @Bat есть более подходящее и дружественное к объектно-ориентированному программированию решение. name () не должно быть окончательным.
— Эндрю Т. Финнелл

вместо for вы можете использовать valueOf (RandomEnum.class, value);
— Войтек

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 06

9
Не перехватывайте общее исключение, так как это может быть OutOfMemory или что-то
— crusy

2
Это ужасная идея. Не последним из них является возможность тихой ошибки без индикации или восстановления. Также теперь значение «имени» и символическое значение в коде (например, A, B) отличаются. +2 за ум. -200 за то, что был ужасно умен. Я бы ни за что не одобрил этот обзор кода.
— pedorro

1
@pedorro Кажется, это не так ужасно, как вы думаете. Любой, кто понимает, что все, что делает комитет Java, не должно восприниматься как Евангелие, прошел бы это в Code Review. Бесконечно хуже переписывать, а затем поддерживать все служебные методы Enum, потому что name () нельзя переопределить. Вместо того, чтобы перехватить исключение и проглотить его, должно быть создано исключение 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;
    }
}

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

Кстати, этот тип ImmutableMap взят из Google guava API, и я определенно рекомендую его в подобных случаях.


РЕДАКТИРОВАТЬ - Согласно комментариям:

  1. Это решение предполагает, что каждое присвоенное строковое значение уникально и не равно нулю. Учитывая, что создатель перечисления может управлять этим, и что строка соответствует уникальному и ненулевому значению перечисления, это кажется безопасным ограничением.
  2. Я добавил метод toSTring (), как просили в вопросе

1
Я получил пару голосов против этого ответа - кто-нибудь хочет рассказать, почему этот ответ плохой? Мне просто любопытно, что думают люди.
— pedorro

Это не сработает для сценария, в котором есть несколько констант перечисления с одним и тем же «значением», например, 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()для строк?
— Betlista

Элегантность не проблема - я согласен, что .equals()все подойдет. Можете использовать, ==если хотите. (Хотя есть веская причина не делать этого, если вы когда-нибудь заменяете перечисление классом.)
— исключение

3
@exception НИКОГДА НЕ ИСПОЛЬЗУЙТЕ == для сравнения строк, поскольку он будет выполнять проверку равенства объектов, в то время как вам нужна проверка равенства содержимого и для этого используйте метод equals () класса String.
— жертву

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.