2009-07-03 2 views
113

Я хотел бы найти перечисление из его строкового значения (или, возможно, любого другого значения). Я пробовал следующий код, но он не позволяет статические в инициализаторах. Есть простой способ?Как я могу просмотреть перечисление Java из его значения String?

public enum Verbosity { 

    BRIEF, NORMAL, FULL; 

    private static Map<String, Verbosity> stringMap = new HashMap<String, Verbosity>(); 

    private Verbosity() { 
     stringMap.put(this.toString(), this); 
    } 

    public static Verbosity getVerbosity(String key) { 
     return stringMap.get(key); 
    } 
}; 
+0

IIRC, который дает NPE, потому что статическая инициализация выполняется сверху вниз (то есть константы перечисления в верхней части строятся до того, как она переходит к инициализации 'stringMap'). Обычным решением является использование вложенного класса. –

+0

Спасибо всем за такой быстрый ответ. (FWIW Я не нашел Sun Javadocs очень полезным для этой проблемы). –

+0

Это проблема языка, а не проблема с библиотекой. Тем не менее, я думаю, что документы API читаются больше, чем JLS (хотя, возможно, не языковые дизайнеры), поэтому подобные вещи, вероятно, должны иметь большее значение в документах java.lang. –

ответ

171

Используйте valueOf метод, который автоматически создается для каждого Enum.

Verbosity.valueOf("BRIEF") == Verbosity.BRIEF 

Для произвольных значений начинаются с:

public static Verbosity findByAbbr(String abbr){ 
    for(Verbosity v : values()){ 
     if(v.abbr().equals(abbr)){ 
      return v; 
     } 
    } 
    return null; 
} 

только перейти позже Карта реализации, если ваш профайлер говорит вам.

Я знаю, что он выполняет итерацию по всем значениям, но только с тремя значениями enum вряд ли стоит каких-либо других усилий, ведь если у вас нет большого количества ценностей, я бы не стал беспокоиться о карте, это будет достаточно быстро.

+0

спасибо - и я могу использовать преобразование case, если знаю, что значения все еще отличаются –

+0

Вы можете напрямую обращаться к члену класса 'abbr', поэтому вместо "v.abbr()" вы можете использовать "v.abbr.equals ... ». – zatziky

+3

Кроме того, для произвольных значений (второй случай) рассмотрите вопрос об отказе 'IllegalArgumentException' вместо возврата null (* i.e.* когда совпадения не найдено) - так JDK 'Enum.valueOf (..)' делает это, во всяком случае. –

6

И вы не можете использовать valueOf()?

Редактировать: Btw, нет ничего, что мешает вам использовать static {} в перечислении.

+0

Или синтетический 'valueOf' в классе enum, поэтому вам не нужно указывать класс enum' Class'. –

+0

Конечно, у него просто не было записи javadoc, поэтому было сложно связать. – Fredrik

+1

вы можете ссылаться на JLS: http://java.sun.com/docs/books/jls/third_edition/html/classes.html#302265 – newacct

103

Вы близко. Для произвольных значений, попробовать что-то вроде следующего:

public enum Day { 

    MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"), 
    THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ; 

    private final String abbreviation; 

    // Reverse-lookup map for getting a day from an abbreviation 
    private static final Map<String, Day> lookup = new HashMap<String, Day>(); 

    static { 
     for (Day d : Day.values()) { 
      lookup.put(d.getAbbreviation(), d); 
     } 
    } 

    private Day(String abbreviation) { 
     this.abbreviation = abbreviation; 
    } 

    public String getAbbreviation() { 
     return abbreviation; 
    } 

    public static Day get(String abbreviation) { 
     return lookup.get(abbreviation); 
    } 
} 
+2

вместо «EnumSet.allOf (Day.class)» вы можете использовать «Day.values ​​()» ... –

+0

А, так я могу. Благодарю. – Lyle

+2

Из-за проблем с загрузкой классов этот метод не будет работать надежно. Я не рекомендую его, и я видел, как он прерывается регулярно, потому что карта поиска не готова до обращения. Вам нужно поместить «поиск» за пределы перечисления или в другой класс, чтобы он загружался первым. –

12

@ Ответ Лайла довольно опасен, и я видел, что он не работает, особенно если вы сделаете enum статическим внутренним классом. Вместо этого я использовал что-то вроде этого, которое будет загружать карты BootstrapSingleton перед перечислениями.

Редактироватьэто не должно быть проблемой больше с современными JVM, (JVM 1.6 или выше), но я думаю, что все еще существуют проблемы с JRebel, но у меня не было шанса его повторное тестирование.

Загрузите меня первый:

public final class BootstrapSingleton { 

     // Reverse-lookup map for getting a day from an abbreviation 
     public static final Map<String, Day> lookup = new HashMap<String, Day>(); 
    } 

Теперь загрузите его в конструкторе перечислений:

public enum Day { 
     MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"), 
     THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ; 

     private final String abbreviation; 

     private Day(String abbreviation) { 
      this.abbreviation = abbreviation; 
      BootstrapSingleton.lookup.put(abbreviation, this); 
     } 

     public String getAbbreviation() { 
      return abbreviation; 
     } 

     public static Day get(String abbreviation) { 
      return lookup.get(abbreviation); 
     } 
    } 

Если у вас есть внутреннее перечисление вы можете просто определить карту выше определения перечислений и (в теория) должны быть загружены раньше.

+2

Необходимо определить «опасный» и почему важна внутренняя реализация класса. Это скорее проблема статического определения «сверху вниз», но есть рабочий код в другом связанном ответе со ссылкой на этот вопрос, который использует статическую карту в экземпляре Enum с методом «get» от пользовательского значения до Тип Enum. http://stackoverflow.com/questions/604424/lookup-enum-by-string-value –

+1

@ DarrellTeague оригинальная проблема была вызвана старыми JVM (до 1.6) или другими JVM (IBM) или swapers для горячего кодирования (JRebel). Проблема не должна возникать в современных JVM, поэтому я могу просто удалить свой ответ. –

+0

Приятно отметить, что это действительно возможно благодаря пользовательским реализациям компилятора и оптимизации времени выполнения ... заказ может быть перенастроен, что приведет к ошибке. Однако это, по-видимому, нарушает спецификацию. –

1

Возможно, взгляните на это. Он работает на меня. Целью этого является поиск «RED» с «/ red_color». Объявление static map и загрузка enum s в него только один раз принесет некоторые преимущества в отношении производительности, если enum s - это много.

public class Mapper { 

public enum Maps { 

    COLOR_RED("/red_color", "RED"); 

    private final String code; 
    private final String description; 
    private static Map<String, String> mMap; 

    private Maps(String code, String description) { 
     this.code = code; 
     this.description = description; 
    } 

    public String getCode() { 
     return name(); 
    } 

    public String getDescription() { 
     return description; 
    } 

    public String getName() { 
     return name(); 
    } 

    public static String getColorName(String uri) { 
     if (mMap == null) { 
      initializeMapping(); 
     } 
     if (mMap.containsKey(uri)) { 
      return mMap.get(uri); 
     } 
     return null; 
    } 

    private static void initializeMapping() { 
     mMap = new HashMap<String, String>(); 
     for (Maps s : Maps.values()) { 
      mMap.put(s.code, s.description); 
     } 
    } 
} 
} 

Пожалуйста, введите свои мнения.

-2

Вы можете использовать функцию Enum::valueOf() как предложил Гарет Дэвис & Брэд Mace выше, но убедитесь, что вы справляетесь IllegalArgumentException, который будет брошен, если строка используется не присутствует в перечислении.

4

В Спецификации языка Java 7 есть a example!который отражает ваш вопрос об инициализации карты с помощью самореференций.

+1

Отлично указывать ссылку, которая также используется в Эффективной Java Joshua Bloch. –

0

Вы можете определить Enum как следующий код:

public enum Verbosity 
{ 
    BRIEF, NORMAL, FULL, ACTION_NOT_VALID; 
    private int value; 

    public int getValue() 
    { 
    return this.value; 
    } 

    public static final Verbosity getVerbosityByValue(int value) 
    { 
    for(Verbosity verbosity : Verbosity.values()) 
    { 
     if(verbosity.getValue() == value) 
      return verbosity ; 
    } 

    return ACTION_NOT_VALID; 
    } 

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

See following link for more clarification

5

с Java 8 вы можете достичь с таким образом:

public static Verbosity findByAbbr(final String abbr){ 
    return Arrays.stream(values()).filter(value -> value.abbr().equals(abbr)).findFirst().orElse(null); 
} 
1

В случае помогает другие, вариант, который я предпочитаю, который здесь не указан, использует Guava's Maps functionality:

public enum Vebosity { 
    BRIEF("BRIEF"), 
    NORMAL("NORMAL"), 
    FULL("FULL"); 

    private String value; 
    private Verbosity(final String value) { 
     this.value = value; 
    } 

    public String getValue() { 
     return this.value; 
    } 

    private static ImmutableMap<String, Verbosity> reverseLookup = 
      Maps.uniqueIndex(Arrays.asList(Verbosity.values)), Verbosity::getValue); 

    public static Verbosity fromString(final String id) { 
     return reverseLookup.getOrDefault(id, NORMAL); 
    } 
} 

С по умолчанию вы можете использовать null, вы можете throw IllegalArgumentException или ваш fromString может вернуть Optional, независимо от поведения вы предпочитаете.

Смежные вопросы