web-dev-qa-db-fra.com

Énumération Java valueOf () avec plusieurs valeurs?

J'ai un problème en Java avec Enums . J'ai lu la documentation sur l'affectation de paramètres de valeur à Enums . Mais ma question est de savoir ce qui concerne les valeurs multiples, est-ce possible?

C'est ce que j'aimerais réaliser: J'ai un Enum pour les langues. Chaque langue est représentée par son nom et par quelques alias plus courts (pas toujours, et pas toujours le même nombre d'alias)

Voici un exemple:

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

Puis-je simplement définir un Enum comme celui-ci et obtenir les bonnes valeurs d'énum en appelant Language.valueOf () ???

43
zolakt

Ceci est probablement similaire à ce que vous essayez d'atteindre.

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;
    }
}

Rappelez-vous que les enums sont une classe comme les autres; English("english", "eng", "en", "en_GB", "en_US") appelle le constructeur enum.

Vous pouvez ensuite récupérer la valeur enum correspondant à une chaîne par le biais d'une méthode de recherche (vous pouvez la redéfinir en tant que méthode statique).

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

Le mappage d'une chaîne sur une valeur enum correspond généralement à ce que la méthode statique valueOf fait pour vous. Donc, si vous voulez accomplir cela avec l'utilisation de synonymes, vous devrez développer quelque chose de similaire. Cependant, nous ne voulons pas donner à la présence la possibilité de remplacer une méthode statique, nous la nommons donc différente pour ce but: fromString devrait être approprié.

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); 
  } 
} 

Comme avantage de ce type d'implémentation, nous pouvons, comme démontré, implémenter aussi facilement la méthode statique has pour vérifier si un alias donné fait partie de l'ensemble de valeurs enum. Dans le même temps, nous avons appliqué de bonnes conventions de dénomination:

  • les valeurs enum vont en majuscules, pour indiquer qu'elles sont en réalité des finales statiques (instances singleton).
  • dans le même temps, nous avons également mis toutes les autres finales statiques tout en majuscules.

Notez qu'il n'est pas nécessaire de répéter le nom de la valeur enum elle-même: nous considérons toujours son propre nom automatiquement (il est ajouté à ALIAS_MAP), et nous normalisons tout en majuscule pour le rendre insensible à la casse.

Ça semble gros, mais en utilisant l'énum, ​​ça a l'air joli:

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

Le conteneur de support des alias est une Map, une HashMap pour être plus spécifique. La HashMap fournit un chemin d'accès rapide aux alias et est également facile à développer. Chaque fois que nous pensons à «indexer» quelque chose, une HashMap devrait être notre premier choix.

Notez que pour une utilisation transparente, nous stockons à la fois le nom de la constante d’énumération elle-même (récupérée via la méthode name()) et tous les alias. Nous aurions pu implémenter cela différemment en essayant d'abord d'effectuer la recherche à l'aide de la méthode valueOf intégrée intégrée. C'est un choix de conception, mais nous aurions potentiellement à traiter avec des exceptions supplémentaires, etc.

14
YoYo

Bref non. 

Le paramètre de la méthode valueOf () doit être uniquement la chaîne du type constant enum. Donc, il ne peut pas varier, ou rechercher des valeurs possibles. Voir le JavaDoc .

Vous devez écrire votre propre méthode d’utilitaire pour renvoyer le type d’énum approprié pour les valeurs données.

3
Andy

Normaliser les données:

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

Ensuite

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

etc.

2
user207421

Wow, réponse très rapide:) Merci les gars.

Andy a raison. Je souhaite appeler Language.valueOf ("eng") ou Language.valueOf ("anglais") et obtenir Language.English as return.

J'ai déjà une fonction utilitaire qui fait cela, mais ce n'est pas très gentil . Fonction de commutation qui vérifie les valeurs de chaîne et renvoie l'instance enum appropriée.

Mais il y a beaucoup de réécriture de code (j'ai environ 30 à 40 langues) ..__ Et si je veux ajouter une langue, je dois l'ajouter à enum et implémenter un nouveau contrôle dans la classe utilitaire.

Je vais essayer l'approche de Flavios ... Une seule question ... Votre constructeur ne devrait-il pas en être ainsi?

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

Vous pouvez également ajouter une méthode pour que vos enums et votre constructeur soient plus propres si la structure est plus compliquée:

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