Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java enum valueOf() with multiple values?

Tags:

java

enums

I have a problem in Java using Enums. I have read the documentation about assigning value parameters to Enums. But, my question is what about multiple values, is it possible?

This what I would like to achieve: I have an Enum for languages. Each language is represented by its name and some shorter aliases (not always, and not always the same number of aliases)

Here is an example:

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

Can I just define an Enum like this and get the right enum values by calling Language.valueOf() ???

like image 501
zolakt Avatar asked Nov 16 '10 19:11

zolakt


People also ask

Can enum have multiple values Java?

Learn to create Java enum where each enum constant may contain multiple values. We may use any of the values of the enum constant in our application code, and we should be able to get the enum constant from any of the values assigned to it.

Can enum have 2 values?

The Enum constructor can accept multiple values.

How does enum valueOf work in Java?

valueOf. Returns the enum constant of the specified enum type with the specified name. The name must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)

Can we use @value in enum?

No, it's not. The purpose for an Enum is to give us mapped data (fixed set of values) with limited scope. If we would to use @Value in Java enums, it would take the purpose of the enum away in the first place.


2 Answers

This is probably similar to what you're trying to achieve.

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

Remember enums are a class like the others; English("english", "eng", "en", "en_GB", "en_US") is calling the enum constructor.

You could then retrieve the enum value corresponding to a string through a search method (you can put it as a static method in the enum again).

public static Language find(String name) {     for (Language lang : Language.values()) {         if (lang.getValues().contains(name)) {             return lang;         }     }     return null; } 
like image 67
Flavio Avatar answered Sep 22 '22 11:09

Flavio


Mapping a string to a enum value, is typically what the valueOf static method is doing for you. So if you want to accomplish this with the use of synonyms, you will have to develop something similar. Because we cannot override a static method, and I do not think we want to in this case, we will name it differently: fromString should be appropriate.

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.toUpperCase());      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);    }  }  

As a benefit of this type of implementation we can, as demonstrated, also easily implement the has static method to test if a given alias is part of the enum value set. At the same time, we applied some good naming conventions:

  • the enum values go in uppercase, to indicate that they are in actuality static finals (singleton instances).
  • at the same time, we also put all the other static finals all caps.

Note that we do not have to repeat the name of the enum value itself: we always consider it's own name automatically (gets added to the ALIAS_MAP), and on top we normalize everything to uppercase to make it case insensitive.

Seems big, but while using the enum, it looks pretty:

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

The backing container for the aliases is a Map, a HashMap to be more specific. The HashMap provides a fast access path to the aliases, and is also easy to grow. Whenever we think about 'indexing' something, likely a HashMap should be our first choice.

Note that for transparent use, we store both the name of the enum constant itself (retrieved through the name() method), and all the aliases. We could have implemented this differently by first attempting to do the lookup using the built-in valueOf static method. It is a design choice, but we would potentially have to deal with additional exceptions etc.

like image 22
10 revs, 2 users 96% Avatar answered Sep 24 '22 11:09

10 revs, 2 users 96%