枚举类型:将字符串转换为枚举类型

3
public enum Gender{

    m("male"), f("female");

    private final String value;

    private Gender(String option){
          value = option;
    }
}

请问我应该如何将字符串"male"转换为任何枚举类型?是的,该值与枚举不同。以下方法无法实现:

 Gender.valueOf("male"); 

我在考虑提供两种方法来解决问题:
1)使用for循环进行解析,或者
2)静态初始化一个Map。
我认为第二种方法更好,因为在初始化Map时,如果存在相同的字符串,就可以抛出运行时异常。这种方法的优缺点是什么?是否有其他更好的解决方案?

1
使用第二个选项更好,而且它被广泛使用了很多次。 - Narendra Pathai
为什么不使用 Gender.valueOf("m")? - BobTheBuilder
因为这些值是在运行时初始化的。m(Constants.male()); @NarendraPathai 有任何示例?或者定义的模式? - seesee
3个回答

5

我会使用地图:

public enum Gender {

    m("male"), f("female");

    private final String value;

    private static final Map<String, Gender> values = new HashMap<String, Gender>();
    static {
        for (Gender g : Gender.values()) {
            if (values.put(g.value, g) != null) {
                  throw new IllegalArgumentException("duplicate value: " + g.value);
            }
        }
    }

    private Gender(String option) {
          value = option;
    }

    public static Gender fromString(String option) {
        return values.get(option);
    }
}

我认为这种方法比第一种方法有两个优点:

  1. 这种方法可以在 O(1) 时间内将字符串转换为 Gender,而另一种方法需要 O(n) 的时间。
  2. 这将自动检测重复值。

我也有同感,只是想确认一下。嘻嘻 - seesee

2
这是一个更为通用的解决方案。我在将枚举映射到代码值时使用它。当您的枚举表示一系列离散值,例如映射到数据库表时,它尤其有帮助。请注意,接下来定义的几个类型只需要编写一次,然后就可以在任何枚举上使用。
首先,您需要定义一个接口,您的枚举(或要存储在集合中的POJO)将实现:
public interface Coded<T> {
    T getCode();
}

以下代码利用了Google Guava,但是你也可以执行空值检查而不使用他们的Optional类:
public final class CodedFinder {

    private CodedFinder() {}

    public static <V, T extends Enum<T> & Coded<V>> T find(final Class<T> target, final V code) {
        final Optional<T> found = findInternal(Arrays.asList(target.getEnumConstants()), code);
        if (! found.isPresent()) {
            throw new IllegalArgumentException(code.toString() + " is invalid for " + target.getSimpleName());
        }
        return found.get();
    }

    // Additional find methods for arrays and iterables redacted for clarity.

    private static <V, T extends Coded<V>> Optional<T> findInternal(final Iterable<T> values, final V code) {
        return Iterables.tryFind(values, CodedPredicate.of(code));
    }
}

上面的方法使用 Class#getEnumConstants 来检索枚举中定义的所有值。实际调用的查找方法不仅可用于枚举,还可用于数组和集合等。
我们需要定义一个 Predicate 来利用 Guava 的查找方法
public final class CodedPredicate<V, T extends Coded<V>> implements com.google.common.base.Predicate<T> {
    private final V value;

    private CodedPredicate(final V value) {
        this.value = value;
    }

    public static <V, T extends Coded<V>> CodedPredicate<V, T> of(final V value) {
        return new CodedPredicate<V, T>(value);
    }

    public boolean apply(final T current) {
        return value.equals(current.getCode());
    }
}

该谓词是通用的,因此您可以使用 Coded<Integer> 或任何其他具有合理 equals() 实现的 POJO。
看起来代码很多,但实际上只定义了三种类型,并且这些类型可以在任意数量的项目之间共享。每当您想要搜索一个值时,它就变得微不足道:
public final class GenderTest {

    @Test(groups="unit")
    public static void testValid() {

        assert CodedFinder.find(Gender.class, "male")  == Gender.m;
        assert CodedFinder.find(Gender.class, "female") == Gender.f;
    }

    @Test(groups="unit", expectedExceptions=IllegalArgumentException.class)
    public static void testInvalid() {

        CodedFinder.find(Gender.class, "foo");
    }

    public enum Gender implements Coded<String> {

        m("male"), f("female");

        private final String value;

        private Gender(final String option) {
              value = option;
        }

        public String getCode()
        {
            return value;
        }
    }
}

哇,真的很好!它看起来有点过度设计,但调用者的代码非常简单。 无论如何,您还应该利用Guava缓存,以便它在O(1)而不是O(n)中运行。 - user1944408

0

我遇到过这种情况几次,通常使用你的第一种方法:

public static Gender forValue(String value) {
    for (Gender gender : gender.values()) {
        if (gender.value.equals(value)) {
            return gender;
        }
    }

    return null; // or throw something like IllegalArgumentException 
}

由于您的value被声明为final,并且所有实例都需要从代码中声明,因此您始终负责确保值是唯一的,因此对我来说,这个问题几乎不存在。


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