简体   繁体   English

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

[英]Enum: Converting String to Enum

public enum Gender{

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

    private final String value;

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

May I know how should I turn a string "male" to any enum? 我是否应该知道如何将字符串“男性”转换为任何枚举? Yes the value is different from the enum. 是的,该值与枚举不同。 this will not work 这不行

 Gender.valueOf("male"); 

I was thinking of providing a 我想提供一个

1) parse with for-loop 1)用for循环解析

or 要么

2) static initialize a Map 2)静态初始化Map

... ...

I feel that the 2nd way is better because when I am initializing the map, I can throw run-time exception if the same String exist. 我觉得第二种方式更好,因为当我初始化地图时,如果存在相同的String,我可以抛出运行时异常。

What are the advantages and disadvantages or is there any other better solution? 有哪些优点和缺点,还是有其他更好的解决方案?

I'd use a map: 我用的是地图:

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

I see two advantages of this over the first approach: 我认为这比第一种方法有两个优点:

  1. This method can convert a string to Gender in O(1) time, whereas the other approach requires O(n) time. 此方法可以在O(1)时间内将字符串转换为Gender ,而另一种方法需要O(n)时间。
  2. This will automatically detect duplicate values. 这将自动检测重复值。

This is a more generalized solution. 这是一个更通用的解决方案。 I use it anytime an enum is mapped to a code value. 我会在枚举映射到代码值的任何时候使用它。 It is especially helpful when you have an enum represent a series of discrete values that map, say, to a database table. 当你有一个枚举表示一系列离散值,例如映射到数据库表时,它会特别有用。 Note that the next few types that are defined only have to be written once and then can be used on any enum. 请注意,定义的下几个类型只需要编写一次,然后可以在任何枚举上使用。

You first define an interface that your enum (or a POJO to be stored in a collection) will implement: 您首先定义一个接口,您的枚举(或要存储在集合中的POJO)将实现:

public interface Coded<T> {
    T getCode();
}

The following leverages Google Guava , however, you could perform null checks rather than using their Optional class: 以下利用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));
    }
}

The method above uses Class#getEnumConstants to retrieve all the values defined in the enum. 上面的方法使用Class#getEnumConstants来检索枚举中定义的所有值。 The actual find method that is being called can be used not just for enums, but also for array and collections, etc. 被调用的实际find方法不仅可以用于枚举,还可以用于数组和集合等。

We have to define a Predicate to leverage Guava's find method : 我们必须定义一个Predicate以利用Guava的find方法

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

That predicate is generic, so you can use Coded<Integer> or any other POJO that has a sensible equals() implementation. 该谓词是通用的,因此您可以使用Coded<Integer>或任何其他具有明智的equals()实现的POJO。

It seems like a lot of code, but really there are only three types defined above, and these can be shared across any number of projects. 它看起来像很多代码,但实际上上面只定义了三种类型,这些类型可以在任意数量的项目中共享。 Whenever you want to search on a value, it becomes trivial: 无论何时你想搜索一个值,它都变得微不足道:

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

I've come across this situation a few times, and usually use your first approach: 我经常遇到过这种情况,通常使用你的第一种方法:

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 
}

Seen as your value is declared final and all instances need to be declared from code, you are always responsible for the values being unique, so for me the issue is close to nonexistent. 看到你的value被声明为final并且所有实例都需要从代码声明, 总是负责值是唯一的,所以对我来说问题几乎不存在。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM