Java enum valueOf() с несколькими значениями?

У меня проблема с Java с помощью Enums. Я прочитал документацию о присвоении параметрам значения Enums. Но, мой вопрос в том, что о нескольких значениях, возможно ли это?

Это то, чего я хотел бы достичь: У меня есть Enum для языков. Каждый язык представлен его именем и более короткими псевдонимами (не всегда и не всегда одинаковым числом псевдонимов)

Вот пример:

public enum Language{
English("english", "eng", "en", "en_GB", "en_US"),
German("german", "de", "ge"),
Croatian("croatian", "hr", "cro"),
Russian("russian")
}

Можно ли просто определить Enum, как это, и получить правильные значения перечисления, вызвав Language.valueOf()???

Ответ 1

Это, вероятно, похоже на то, что вы пытаетесь достичь.

public enum Language{
    English("english", "eng", "en", "en_GB", "en_US"),
    German("german", "de", "ge"),
    Croatian("croatian", "hr", "cro"),
    Russian("russian");

    private final List<String> values;

    Language(String ...values) {
        this.values = Arrays.asList(values);
    }

    public List<String> getValues() {
        return values;
    }
}

Помните, что перечисления - это класс, подобный другим; English("english", "eng", "en", "en_GB", "en_US") вызывает конструктор перечисления.

Затем вы можете получить значение перечисления, соответствующее строке с помощью метода поиска (вы можете поместить его как статический метод в перечисление снова).

public static Language find(String name) {
    for (Language lang : Language.values()) {
        if (lang.getValues().contains(name)) {
            return lang;
        }
    }
    return null;
}

Ответ 2

Сопоставление строки с значением перечисления, как правило, используется для статического метода valueOf. Поэтому, если вы хотите добиться этого с помощью синонимов, вам придется разработать нечто подобное. Однако мы не хотим указывать, что мы можем переопределить статический метод, поэтому для этой цели мы просто назовем его другим: fromString должен быть соответствующим.

public enum Language { 
  ENGLISH("eng", "en", "en_GB", "en_US"),   
  GERMAN("de", "ge"),   
  CROATIAN("hr", "cro"),   
  RUSSIAN("ru"),
  BELGIAN("be",";-)");

  static final private Map<String,Language> ALIAS_MAP = new HashMap<String,Language>(); 
  static { 
    for (Language language:Language.values()) { 
      // ignoring the case by normalizing to uppercase
      ALIAS_MAP.put(language.name().toUpperCase(),language); 
      for (String alias:language.aliases)
        ALIAS_MAP.put(alias.toUpperCase(),language);
    } 
  } 

  static public boolean has(String value) { 
    // ignoring the case by normalizing to uppercase
    return ALIAS_MAP.containsKey(value.toUpperCase());
  } 

  static public Language fromString(String value) { 
    if (value == null) throw new NullPointerException("alias null"); 
    Language language = ALIAS_MAP.get(value); 
    if (language == null)
      throw new IllegalArgumentException("Not an alias: "+value); 
    return language; 
  } 

  private List<String> aliases; 
  private Language(String... aliases) { 
    this.aliases = Arrays.asList(aliases); 
  } 
} 

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

  • значения перечисления идут в верхнем регистре, чтобы указать, что они находятся в актуальном статическом финале (экземпляры singleton).
  • в то же время мы также ставим все остальные статические финалы на все шапки.

Обратите внимание, что нам не нужно повторять имя самого значения перечисления: мы всегда считаем его собственное имя автоматически (добавляется в ALIAS_MAP), и сверху мы нормализуем все в верхнем регистре, чтобы сделать регистр нечувствительным.

Кажется большим, но при использовании перечисления он выглядит довольно:

public void main() {
  Language myLanguage = Language.fromString("en_GB");
  if (myLanguage == Language.ENGLISH) {
    System.out.println("Yes, I know, you understand English!");
  }
} 

Контейнер поддержки для псевдонимов - это Map, a HashMap, чтобы быть более конкретным. HashMap обеспечивает быстрый доступ к псевдонимам, а также легко растет. Всякий раз, когда мы думаем об "индексировании" чего-то, скорее всего, HashMap должен быть нашим первым выбором.

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

Ответ 3

Короче говоря, нет.

Параметр метода valueOf() должен быть только строкой константного типа enum. Поэтому он не может меняться или искать возможные значения. См. JavaDoc.

Вам нужно написать свой собственный метод утилиты, чтобы вернуть правильный тип перечисления для заданных значений.

Ответ 5

Нормализовать данные:

public enum LanguageCode
{
  ENGLISH,
  GERMAN,
  CROATIAN,
  RUSSIAN,
  // ...
}
// (add whatever initialization you want to that

Тогда

public enum Language{
  english(ENGLISH),
  eng(ENGLISH),
  en(ENGLISH),
  en_GB(ENGLISH),
  // ...
}

и др.

Ответ 6

Ничего себе, очень быстрый ответ:) Спасибо, ребята.

Энди прав. Я хочу вызвать Language.valueOf( "eng" ) или Language.valueOf( "english" ) и получить Language.English в качестве возврата.

У меня уже есть функция утилиты, которая делает это, но это не очень приятно. Функция переключателя, которая проверяет строковые значения и возвращает соответствующий экземпляр enum.

Но есть много перекодировки кода (у меня есть примерно 30-40 языков). И если я хочу добавить язык, я должен добавить его в enum и реализовать новую проверку в классе утилиты.

Я попробую Flavios. Только один вопрос. Ваш конструктор, не так ли?

Language(List<String> values) {
    this.values = Arrays.asList(values);
}

Ответ 7

Альтернативно добавьте метод, поэтому ваши перечисления и конструктор могут быть более чистыми, если структура сложнее:

public Language getLanguage(String code){
  switch(code){
    case "en":
    case "en_GB":
    ...
    case "eng":
      return ENGLISH;
    case "rus":
    case "russian":
      return RUSSIAN;
  }
}