如何从Java中的字符串值获得枚举值?

How to get an enum value from a string value in Java?

假设我有一个枚举

1
2
3
public enum Blah {
    A, B, C, D
}

我想找到一个字符串的枚举值,例如"A",它是Blah.A。怎么可能做到这一点?

我需要的方法是Enum.valueOf()吗?如果是的话,我怎么用这个?


是的,Blah.valueOf("A")会给你Blah.A

注意,名称必须完全匹配,包括case:Blah.valueOf("A")Blah.valueOf("A")都抛出IllegalArgumentException

静态方法valueOf()values()是在编译时创建的,不会出现在源代码中。不过,它们确实出现在javadoc中;例如,Dialog.ModalityType显示了这两种方法。


如果文本与枚举值不同,则另一个解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public enum Blah {
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;

    Blah(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public static Blah fromString(String text) {
        for (Blah b : Blah.values()) {
            if (b.text.equalsIgnoreCase(text)) {
                return b;
            }
        }
        return null;
    }
}


我使用的是一个漂亮的实用程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * A common method for all enums since they can't have another base class
 * @param <T> Enum type
 * @param c enum type. All enums must be all caps.
 * @param string case insensitive
 * @return corresponding enum, or null
 */

public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
    if( c != null && string != null ) {
        try {
            return Enum.valueOf(c, string.trim().toUpperCase());
        } catch(IllegalArgumentException ex) {
        }
    }
    return null;
}

然后在我的枚举类中,我通常使用这个来保存一些输入:

1
2
3
public static MyEnum fromString(String name) {
    return getEnumFromString(MyEnum.class, name);
}

如果您的Enum不是全部大写,只需更改Enum.valueOf行即可。

太糟糕了,我不能用T.class代替Enum.valueOf,因为T被删除了。


你也应该小心处理你的案件。让我解释一下:做Blah.valueOf("A")是可行的,但Blah.valueOf("A")是行不通的。然后,Blah.valueOf("a".toUpperCase(Locale.ENGLISH))又会起作用。

编辑基于tc将toUpperCase改为toUpperCase(Locale.ENGLISH)。注释与Java文档

编辑2苏莱指出,在安卓系统上,你应该使用Locale.US


使用Joshua Bloch的模式,有效的Java:

(为了简洁而简化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
enum MyEnum {
    ENUM_1("A"),
    ENUM_2("B");

    private String name;

    private static final Map<String,MyEnum> ENUM_MAP;

    MyEnum (String name) {
        this.name = name;
    }

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

    // Build an immutable map of String name to enum pairs.
    // Any Map impl can be used.

    static {
        Map<String,MyEnum> map = new ConcurrentHashMap<String, MyEnum>();
        for (MyEnum instance : MyEnum.values()) {
            map.put(instance.getName(),instance);
        }
        ENUM_MAP = Collections.unmodifiableMap(map);
    }

    public static MyEnum get (String name) {
        return ENUM_MAP.get(name);
    }
}

还可以看到:

使用枚举和实例映射的Oracle Java示例

枚举类型中静态块的执行顺序

如何从字符串值中查找JavaEnUM


这里有一个方法可以对任何枚举执行此操作,并且不区分大小写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * Finds the value of the given enumeration by name, case-insensitive.
 * Throws an IllegalArgumentException if no match is found.  
 **/

public static <T extends Enum<T>> T valueOfIgnoreCase(
        Class<T> enumeration, String name) {

    for (T enumValue : enumeration.getEnumConstants()) {
        if (enumValue.name().equalsIgnoreCase(name)) {
            return enumValue;
        }
    }

    throw new IllegalArgumentException(String.format(
       "There is no value with name '%s' in Enum %s",
        name, enumeration.getName()
    ));
}


使用Blah.valueOf(string)是最好的,但也可以使用Enum.valueOf(Blah.class, string)


如果您不想编写自己的实用程序,请使用谷歌的guava库:

1
Enums.getIfPresent(Blah.class,"A")

与内置的Java函数不同,它让我们检查是否存在BLAH,并且不会抛出异常。


这里我的2分:使用Java8流+检查精确的字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public enum MyEnum {
    VALUE_1("Super"),
    VALUE_2("Rainbow"),
    VALUE_3("Dash"),
    VALUE_3("Rocks");

    private final String value;

    MyEnum(String value) {
        this.value = value;
    }

    /**
     * @return the Enum representation for the given string.
     * @throws IllegalArgumentException if unknown string.
     */

    public static MyEnum fromString(String s) throws IllegalArgumentException {
        return Arrays.stream(MyEnum.values())
                .filter(v -> v.value.equals(s))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("unknown value:" + s));
    }
}

**编辑**

将函数改名为EDCOX1×4,因为使用该约定命名它,您将从Java语言本身获得一些好处;例如:

  • 在HeaderParam注释处直接转换类型

  • 在Java 8或更高版本中,使用流:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public enum Blah
    {
        A("text1"),
        B("text2"),
        C("text3"),
        D("text4");

        private String text;

        Blah(String text) {
            this.text = text;
        }

        public String getText() {
            return this.text;
        }

        public static Blah fromText(String text) {
            return Arrays.stream(values())
              .filter(bl -> bl.text.equalsIgnoreCase(text))
              .findFirst()
              .orElse(null);
        }
    }

    您可能需要这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public enum ObjectType {
        PERSON("Person");

        public String parameterName;

        ObjectType(String parameterName) {
            this.parameterName = parameterName;
        }

        public String getParameterName() {
            return this.parameterName;
        }

        //From String method will return you the Enum for the provided input string
        public static ObjectType fromString(String parameterName) {
            if (parameterName != null) {
                for (ObjectType objType : ObjectType.values()) {
                    if (parameterName.equalsIgnoreCase(objType.parameterName)) {
                        return objType;
                    }
                }
            }
            return null;
        }
    }

    再加一个:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
       public static String fromEnumName(String parameterName) {
            if (parameterName != null) {
                for (DQJ objType : DQJ.values()) {
                    if (parameterName.equalsIgnoreCase(objType.name())) {
                        return objType.parameterName;
                    }
                }
            }
            return null;
        }

    这将按字符串化的枚举名称返回值,例如,如果在fromEnumName中提供"person",它将返回枚举的值,即"person"。


    另一种方法是使用枚举的隐式静态方法name()。name将返回用于创建枚举的准确字符串,该枚举可用于检查提供的字符串:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public enum Blah {

        A, B, C, D;

        public static Blah getEnum(String s){
            if(A.name().equals(s)){
                return A;
            }else if(B.name().equals(s)){
                return B;
            }else if(C.name().equals(s)){
                return C;
            }else if (D.name().equals(s)){
                return D;
            }
            throw new IllegalArgumentException("No Enum specified for this string");
        }
    }

    测试:

    System.out.println(Blah.getEnum("B").name());

    1
    //it will print B  B

    灵感:Java中枚举的10个例子


    使用guava库的解决方案。方法getPlanet()不区分大小写,因此GetPlanet("Mercury")将返回Planet.Mercury。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.universe.solarsystem.planets;
    import org.apache.commons.lang3.StringUtils;
    import com.google.common.base.Enums;
    import com.google.common.base.Optional;

    //Pluto and Eris are dwarf planets, who cares!
    public enum Planet {
       MERCURY,
       VENUS,
       EARTH,
       MARS,
       JUPITER,
       SATURN,
       URANUS,
       NEPTUNE;

       public static Planet getPlanet(String name) {
          String val = StringUtils.trimToEmpty(name).toUpperCase();
          Optional <Planet> possible = Enums.getIfPresent(Planet.class, val);
          if (!possible.isPresent()) {
             throw new IllegalArgumentException(val +"? There is no such planet!");
          }
          return possible.get();
       }
    }

    为了增加前面的答案,并解决一些关于空值和NPE的讨论,我使用了guava选项来处理缺席/无效的案例。这对于URI/参数解析非常有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public enum E {
        A,B,C;
        public static Optional<E> fromString(String s) {
            try {
                return Optional.of(E.valueOf(s.toUpperCase()));
            } catch (IllegalArgumentException|NullPointerException e) {
                return Optional.absent();
            }
        }
    }

    对于那些不知道的人,这里有一些关于使用可选选项避免空值的更多信息:https://code.google.com/p/guava-libraries/wiki/usingandvoidingnullExplained可选


    在Java 8中,静态映射模式更容易,是我的预处理方法。如果要将枚举与Jackson一起使用,可以重写toString并使用它而不是名称,然后使用@JsonValue进行注释。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public enum MyEnum {
        BAR,
        BAZ;
        private static final Map<String, MyEnum> MAP = Stream.of(MyEnum.values()).collect(Collectors.toMap(Enum::name, Function.identity()));
        public static MyEnum fromName(String name){
            return MAP.get(name);
        }
    }

    public enum MyEnumForJson {
        BAR("bar"),
        BAZ("baz");
        private static final Map<String, MyEnumForJson> MAP = Stream.of(MyEnumForJson.values()).collect(Collectors.toMap(Object::toString, Function.identity()));
        private final String value;

        MyEnumForJson(String value) {
            this.value = value;
        }

        @JsonValue
        @Override
        public String toString() {
            return value;
        }

        public static MyEnumForJson fromValue(String value){
            return MAP.get(value);
        }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public static MyEnum getFromValue(String value) {
        MyEnum resp = null;
        MyEnum nodes[] = values();
        for(int i = 0; i < nodes.length; i++) {
            if(nodes[i].value.equals(value)) {
                resp = nodes[i];
                break;
            }
        }
        return resp;
    }


    o(1)方法的灵感来源于节俭的代码,该代码使用哈希图。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public enum USER {
            STUDENT("jon",0),TEACHER("tom",1);

            private static final Map<String, Integer> map = new HashMap<>();

            static {
                    for (USER user : EnumSet.allOf(USER.class)) {
                            map.put(user.getTypeName(), user.getIndex());
                    }
            }

            public static int findIndexByTypeName(String typeName) {
                    return map.get(typeName);
            }

            private USER(String typeName,int index){
                    this.typeName = typeName;
                    this.index = index;
            }
            private String typeName;
            private int index;
            public String getTypeName() {
                    return typeName;
            }
            public void setTypeName(String typeName) {
                    this.typeName = typeName;
            }
            public int getIndex() {
                    return index;
            }
            public void setIndex(int index) {
                    this.index = index;
            }

    }

    Apache的commons lang库有一个静态函数org.apache.commons.lang3.enumutils.getenum,它将字符串映射到枚举类型。基本上和杰弗里斯的答案一样,但为什么当它已经在野外的时候,还要自己滚呢?


    添加到最热门的答案,有一个有用的实用程序…

    valueOf()在不喜欢其输入的情况下抛出两个不同的异常。

    • IllegalArgumentException
    • NullPointerExeption

    如果您的要求是这样的,您没有任何保证您的字符串一定会匹配枚举值,例如,如果字符串数据来自数据库并且可能包含枚举的旧版本,那么您需要经常处理这些…

    所以这里有一个我编写的可重用方法,它允许我们定义一个默认的枚举,如果我们传递的字符串不匹配,它将被返回。

    1
    2
    3
    4
    5
    6
    7
    private static <T extends Enum<T>> T valueOf( String name , T defaultVal) {
            try {
                return Enum.valueOf(defaultVal.getDeclaringClass() , name);
            } catch (IllegalArgumentException | NullPointerException e) {
                return defaultVal;
            }
        }

    这样使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public enum MYTHINGS {
        THINGONE,
        THINGTWO
    }

    public static void main(String [] asd) {
      valueOf("THINGTWO" , MYTHINGS.THINGONE);//returns MYTHINGS.THINGTWO
      valueOf("THINGZERO" , MYTHINGS.THINGONE);//returns MYTHINGS.THINGONE
    }


    EDCOX1 22定义了几种有用的方法,这些方法对于Java中的所有枚举类型都是可用的:

    • 可以使用name()方法获取任何枚举常量的名称。用于写入枚举常量的字符串文字是它们的名称。
    • 同样,可以使用values()方法从枚举类型中获取所有枚举常量的数组。
    • 对于被问及的问题,如图所示,可以使用EDCOX1×5的方法将Java中的任何字符串转换为EnUM常量。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class EnumDemo06 {
        public static void main(String args[]) {
            Gender fromString = Gender.valueOf("MALE");
            System.out.println("Gender.MALE.name() :" + fromString.name());
        }

        private enum Gender {
            MALE, FEMALE;
        }
    }

    Output:
    Gender.MALE.name() : MALE

    在这段代码中,valueOf()方法返回一个枚举常量gender.male,调用它的名称返回"MALE"


    怎么样?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public enum MyEnum {
        FIRST,
        SECOND,
        THIRD;

        public static Optional<MyEnum> fromString(String value){
            try{
                return Optional.of(MyEnum.valueOf(value));
            }catch(Exception e){
                return Optional.empty();
            }
        }
    }

    以相反的方式捕获另一个实用程序。使用标识该枚举的值,而不是其名称。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.EnumSet;

    public class EnumUtil {

        /**
         * Returns the <wyn>Enum</wyn> of type <wyn>enumType</wyn> whose a
         * public method return value of this Enum is
         * equal to <wyn>valor</wyn>.<br/>
         * Such method should be unique public, not final and static method
         * declared in Enum.
         * In case of more than one method in match those conditions
         * its first one will be chosen.
         *
         * @param enumType
         * @param value
         * @return
         */

        public static <E extends Enum<E>> E from(Class<E> enumType, Object value) {
            String methodName = getMethodIdentifier(enumType);
            return from(enumType, value, methodName);
        }

        /**
         * Returns the <wyn>Enum</wyn> of type <wyn>enumType</wyn> whose  
         * public method <wyn>methodName</wyn> return is
         * equal to <wyn>value</wyn>.<br/>
         *
         * @param enumType
         * @param value
         * @param methodName
         * @return
         */

        public static <E extends Enum<E>> E from(Class<E> enumType, Object value, String methodName) {
            EnumSet<E> enumSet = EnumSet.allOf(enumType);
            for (E en : enumSet) {
                try {
                    String invoke = enumType.getMethod(methodName).invoke(en).toString();
                    if (invoke.equals(value.toString())) {
                        return en;
                    }
                } catch (Exception e) {
                    return null;
                }
            }
            return null;
        }

        private static String getMethodIdentifier(Class<?> enumType) {
            Method[] methods = enumType.getDeclaredMethods();
            String name = null;
            for (Method method : methods) {
                int mod = method.getModifiers();
                if (Modifier.isPublic(mod) && !Modifier.isStatic(mod) && !Modifier.isFinal(mod)) {
                    name = method.getName();
                    break;
                }
            }
            return name;
        }
    }

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public enum Foo {
        ONE("eins"), TWO("zwei"), THREE("drei");

        private String value;

        private Foo(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }

    EnumUtil.from(Foo.class,"drei")返回Foo.THREE,因为它将使用getValue来匹配"drei",这是foo中唯一的公共方法,不是最终方法,也不是静态方法。如果foo有超过public、非final、非static的方法,例如返回"drei"的getTranslate,则可以使用另一种方法:EnumUtil.from(Foo.class,"drei","getTranslate")


    由于switch版本还没有被提到,所以我介绍了它(重用op的枚举):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
      private enum Blah {
        A, B, C, D;

        public static Blah byName(String name) {
          switch (name) {
            case"A":
              return A;
            case"B":
              return B;
            case"C":
              return C;
            case"D":
              return D;
            default:
              throw new IllegalArgumentException(
               "No enum constant" + Blah.class.getCanonicalName() +"." + name);
          }
        }
      }

    因为这不会给valueOf(String name)方法提供任何额外的值,所以只有在我们希望有不同的行为时,才有必要定义一个额外的方法。如果我们不想提出一个IllegalArgumentException,我们可以将实现更改为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
      private enum Blah {
        A, B, C, D;

        public static Blah valueOfOrDefault(String name, Blah defaultValue) {
          switch (name) {
            case"A":
              return A;
            case"B":
              return B;
            case"C":
              return C;
            case"D":
              return D;
            default:
              if (defaultValue == null) {
                throw new NullPointerException();
              }
              return defaultValue;
          }
        }
      }

    通过提供违约价值,我们保留了Enum.valueOf(String name)的合同,而不以不返还null的方式抛出IllegalArgumentException。因此,如果名称为null,我们抛出NullPointerException;如果名称为default,我们抛出NullPointerException;如果名称为defaultValue,我们抛出null。这就是valueOfOrDefault的工作原理。

    该方法采用EDCOX1×17接口的设计,提供了Java 8的EDOCX1×18的方法。


    获取枚举名称的最快方法是在应用程序启动时创建枚举文本和值的映射,并获取名称调用函数blah.getEnumName():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public enum Blah {
        A("text1"),
        B("text2"),
        C("text3"),
        D("text4");

        private String text;
        private HashMap<String, String> map;
        Blah(String text) {
        this.text = text;
        }

        public String getText() {
          return this.text;
        }

        static{
          createMapOfTextAndName();
        }

        public static void createMapOfTextAndName() {
            map = new HashMap<String, String>();
            for (Blah b : Blah.values()) {
                 map.put(b.getText(),b.toString());
            }
        }
        public static String getEnumName(String text) {
            return map.get(text.toLowerCase());
        }
    }


    我喜欢使用这种过程将命令作为字符串解析为枚举。我通常将其中一个枚举设置为"未知",因此在未找到其他枚举时返回(即使是在不区分大小写的基础上),而不是空枚举(这意味着没有值)会有所帮助。因此我使用这种方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    static <E extends Enum<E>> Enum getEnumValue(String what, Class<E> enumClass) {
        Enum<E> unknown=null;
        for (Enum<E> enumVal: enumClass.getEnumConstants()) {  
            if (what.compareToIgnoreCase(enumVal.name()) == 0) {
                return enumVal;
            }
            if (enumVal.name().compareToIgnoreCase("unknown") == 0) {
                unknown=enumVal;
            }
        }  
        return unknown;
    }