如何将重写了toString()方法的枚举类型的字符串结果转换回枚举类型?

21

考虑以下 Java 枚举类型:

public enum AgeRange {

   A18TO23 {
        public String toString() {        
            return "18 - 23";
        }
    },
   A24TO29 {
        public String toString() {        
            return "24 - 29";
        }
    },
   A30TO35 {
        public String toString() {        
            return "30 - 35";
        }
    },

}

有没有办法将字符串值 "18 - 23" 转换为相应的枚举值,即 AgeRange.A18TO23?

谢谢!

6个回答

31

最好和最简单的方法是这样的:

public enum AgeRange {
    A18TO23 ("18-23"),
    A24TO29 ("24-29"),
    A30TO35("30-35");

    private String value;

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

    public String toString(){
        return value;
    }

    public static AgeRange getByValue(String value){
        for (final AgeRange element : EnumSet.allOf(AgeRange.class)) {
            if (element.toString().equals(value)) {
                return element;
            }
        }
        return null;
    }
}

然后你只需要调用 getByValue() 方法并将 String 输入作为参数传入。


1
我同意在构造函数中设置值比较好。对于非常大的枚举(确实需要非常大),使用映射可能是有意义的。个人而言,我也会在循环内部返回,但我从未喜欢“无论可读性如何,都要从一个地方返回”的做法 :) - Jon Skeet
另外一个要点 - 最好使用EnumSet.allOf而不是AgeRange.values(),否则你会为每个调用创建一个新的数组。 - Jon Skeet
非常有趣。代码中发生的许多事情对我来说都是新的。谢谢你的提示! - Kevin
你觉得,与其使用 getByValue(String) 方法,不如直接委托给枚举类固有的 valueOf(String) 方法?参考:https://dev59.com/3mct5IYBdhLWcg3wqfL9 - liltitus27

7
您可以始终创建一个从字符串到值的映射 - 静态地执行此操作,因此您只需要映射一次,假设返回的字符串随时间保持不变。据我所知,没有内置的功能可以实现这一点。

这是一种更好的处理方式。当枚举被创建时,地图也随之创建,并且比迭代数组操作速度更快。 - Spencer Kormos
我也在考虑这种方法,但是我的枚举类型只有大约15个值。创建一个映射表是否仍然更有效率? - Kevin
说实话,可能不需要。哈希表适用于大量数据,但我怀疑比较15个值(最坏情况下)的速度与获取哈希码、找到正确的桶等操作差不多。 - Jon Skeet

4
根据《Effective Java(第2版)》的第30条,它可以实现(比循环要快得多)。
public enum AgeRange {
       A18TO23("18-23"),
       A24TO29("24-29"),
       A30TO35("30-35");

       private final String value;

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

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

       private static final Map<String, AgeRange> stringToEnum =
           new HashMap<String, AgeRange>();

       static {
           for (AgeRange r : values()) {
               stringToEnum.put(r.toString(), r);
           }
       }

       public static AgeRange getByValue(String value){
           return stringToEnum.get(value);
       }
}

2
for (AgeRange ar: EnumSet.allOf(AgeRange)) {
    if (ar.toString().equals(inString)) {
         myAnswer = ar;
         break;
    }
}

或者类似这样?只是打了进去,还没有通过编译器。请原谅(评论)错字……

或者使用这样的逻辑来构建一张地图。在运行时避免迭代。好主意,Jon。


2
该类重写了“toString ()”-因此,要获得相反的操作,您需要重写valueof()toString()的输出转换回Enum值。
public enum AgeRange {

   A18TO23 {
        public String toString() {        
                return "18 - 23";
        }
        public AgeRange valueOf (Class enumClass, String name) {
                return A18T023
        }
    },

    .
    .
    .
}

购买者需谨慎 - 未编译和未测试...

toString()valueOf() 的机制是API的一个已记录部分。


那个第二个应该是valueOf方法吗? - iny
2
如果我没记错的话,这里关于 valueOf() 的建议是错误的,说得委婉一点。valueOf() 是静态的,在特定枚举值的主体中没有用处。即使所有这些问题都得到了解决,“正确”的实现也是错误的,因为编译器不允许在 static public valueOf(String) 上进行静态覆盖。常见的解决方法是简单地不使用 valueOf - 使用其他名称。 - Ed Staub

0
你可以尝试以下的代码?
static AgeRange fromString(String range) {
    for (AgeRange ageRange : values()) {
        if (range.equals(ageRange.toString())) {
            return ageRange;
        }
    }
    return null;   
}

或者,正如其他人建议的那样,采用缓存方法:

private static Map<String, AgeRange> map;

private static synchronized void registerAgeRange(AgeRange ageRange) {
    if (map == null) {
        map = new HashMap<String, AgeRange>();
    }
    map.put(ageRange.toString(), ageRange);
}

AgeRange() {
    registerAgeRange(this);
}

static AgeRange fromString(String range) {
    return map.get(range);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接