简体   繁体   English

如何在java中将一个枚举转换为另一个枚举?

[英]How to convert one enum to another enum in java?

I have; 我有;

public enum Detailed {
    PASSED, INPROCESS, ERROR1, ERROR2, ERROR3;
}

and need to convert it to the following; 并需要将其转换为以下内容;

public enum Simple {
    DONE, RUNNING, ERROR;
}

So first PASSED -> DONE and INPROCESS -> RUNNING , but all errors should be: ERROR . 所以首先PASSED - > DONEINPROCESS - > RUNNING ,但所有错误都应该是: ERROR Obviously it is possible to write cases for all values, but there may be a better solution? 显然,可以为所有值编写案例,但可能有更好的解决方案?

Personally I would just create a Map<Detailed, Simple> and do it explicitly - or even use a switch statement, potentially. 就个人而言,我只想创建一个Map<Detailed, Simple>并明确地执行它 - 甚至可能使用switch语句。

Another alternative would be to pass the mapping into the constructor - you could only do it one way round, of course: 另一种方法是将映射传递给构造函数 - 当然,你只能单向循环:

public enum Detailed {
    PASSED(Simple.DONE),
    INPROCESS(Simple.RUNNING),
    ERROR1(Simple.ERROR),
    ERROR2(Simple.ERROR),
    ERROR3(Simple.ERROR);

    private final Simple simple;

    private Detailed(Simple simple) {
        this.simple = simple;
    }

    public Simple toSimple() {
        return simple;
    }
}

(I find this simpler than Ted's approach of using polymorphism, as we're not really trying to provide different behaviour - just a different simple mapping.) (我觉得这比Ted使用多态的方法更简单,因为我们并没有真正尝试提供不同的行为 - 只是一个不同的简单映射。)

While you could potentially do something cunning with the ordinal value, it would be much less obvious, and take more code - I don't think there'd be any benefit. 虽然你可能会使用序数值做一些狡猾的事情,但它会更不明显,并且需要更多的代码 - 我认为没有任何好处。

One way is to define a method asSimple() in your Detailed enum: 一种方法是在您的Detailed枚举中定义一个方法asSimple()

public enum Detailed {
    PASSED {
        @Override
        Simple asSimple() {
            return DONE;
        }
    },
    INPROCESS {
        @Override
        Simple asSimple() {
            return RUNNING;
        }
    },
    ERROR1,
    ERROR2,
    ERROR3;
    public Simple asSimple() {
        return Simple.ERROR; // default mapping
    }
}

You can then simply call the method when you want to do the mapping: 然后,您可以在想要进行映射时简单地调用该方法:

Detailed code = . . .
Simple simpleCode = code.asSimple();

It has the advantage of putting the knowledge of the mapping with the Detailed enum (where perhaps it belongs). 它具有将映射知识与Detailed枚举(可能属于它)放在一起的优点。 It has the disadvantage of having knowledge of Simple mixed in with the code for Detailed . 它的缺点是具有Simple知识和Detailed的代码。 This may or may not be a bad thing, depending on your system architecture. 根据您的系统架构,这可能是也可能不是坏事。

Use EnumMap 使用EnumMap

I decouple my external xml interface from my internal domain model by implementing a transformation service. 我通过实现转换服务将外部xml接口与内部域模型分离。 This includes mapping enums from jaxb generated code to domain model enums. 这包括将jaxb生成的代码中的枚举映射到域模型枚举。

Using a static EnumMap encapsulates the concern of transformation within the class responsible for transformation. 使用静态EnumMap封装了负责转换的类中转换的关注点。 Its cohesive. 它的凝聚力。

@Service
public class XmlTransformer {

    private static final Map<demo.xml.Sense, Constraint.Sense> xmlSenseToSense;
    static {
        xmlSenseToSense = new EnumMap<demo.xml.Sense, Constraint.Sense> (
            demo.xml.Sense.class);
        xmlSenseToSense.put(demo.xml.planningInterval.Sense.EQUALS, 
            Constraint.Sense.EQUALS);
        xmlSenseToSense.put(demo.xml.planningInterval.Sense.GREATER_THAN_OR_EQUALS, 
            Constraint.Sense.GREATER_THAN_OR_EQUALS);
        xmlSenseToSense.put(demo.xml.planningInterval.Sense.LESS_THAN_OR_EQUALS, 
            Constraint.Sense.LESS_THAN_OR_EQUALS);
    }
    ...
}

Ted's answer is very Javaly, but the expression 特德的答案非常贾瓦利,但表达方式

passed == PASSED ? DONE : ERROR

would do the job, too. 也会做这个工作。

To me that sounds more like a conceptual problem than a programming problem. 对我而言,这听起来更像是概念问题,而不是编程问题。 Why don't you just remove the "Simple" enum type and use the other one instead in all places in the program? 为什么不直接删除“简单”枚举类型并在程序的所有位置使用另一个?

Just to make that more clear with another example: Would you really try to define an enum type for the work days in a week (Monday to Friday) and another enum for all days of a week (Monday to Sunday)? 只是为了让另一个例子更清楚:你真的会尝试为一周(星期一到星期五)的工作日和一周的所有日子(星期一到星期日)定义一个枚举类型吗?

Here is the simple enum mapper with test: 这是带有test的简单枚举映射器:

-- IMPLEMENTATION - 实施

-- ENUMS - ENUMS

public enum FirstEnum {

A(0), B(1);

private final int value;

private FirstEnum(int value) {
    this.value = value;
}

public int getValue() {
    return value;
}
}

public enum  SecondEnum {

C(0), D(1);

private final int valueId;

private SecondEnum(int valueId) {
    this.valueId = valueId;
}

public int getValueId() {
    return valueId;
}

}

--MAPPER --MAPPER

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.Validate;

import com.google.common.collect.Sets;

public class EnumPropertyMapping {

private final Map<?, ?> firstMap;
private final Map<?, ?> secondMap;

private final Class<?> firstType;
private final Class<?> secondType;

private EnumPropertyMapping(
        Map<?, ?> firstMap, Map<?, ?> secondMap, Class<?> firstType, Class<?> secondType) {

    this.firstMap = firstMap;
    this.secondMap = secondMap;
    this.firstType = firstType;
    this.secondType = secondType;
}

public static Builder builder() {
    return new Builder();
}

@SuppressWarnings("unchecked")
public <R> R getCorrespondingEnum(Object mappedEnum) {
    Validate.notNull(mappedEnum, "Enum must not be NULL");
    Validate.isInstanceOf(Enum.class, mappedEnum, "Parameter must be an Enum");

    if (firstType.equals(mappedEnum.getClass())) {
        return (R) firstMap.get(mappedEnum);
    }

    if (secondType.equals(mappedEnum.getClass())) {
        return (R) secondMap.get(mappedEnum);
    }

    throw new IllegalArgumentException("Didn't found mapping for enum value: " + mappedEnum);
}

public static class Builder {

    private final Map<Object, Object> firstEnumMap = new HashMap<>();
    private final Map<Object, Object> secondEnumMap = new HashMap<>();
    private Class<?> firstEnumType;
    private Class<?> secondEnumType;

    public <T extends Enum<T>> Builder addFirst(Class<T> enumType, String propertyName) {
        firstEnumType = enumType;
        initMap(firstEnumMap, enumType.getEnumConstants(), propertyName);
        return this;
    }

    public <T extends Enum<T>> Builder addSecond(Class<T> enumType, String propertyName) {
        secondEnumType = enumType;
        initMap(secondEnumMap, enumType.getEnumConstants(), propertyName);
        return this;
    }

    private void initMap(Map<Object, Object> enumMap, Object[] enumConstants, String propertyName) {
        try {
            for (Object constant : enumConstants) {
                enumMap.put(PropertyUtils.getProperty(constant, propertyName), constant);
            }
        } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ex) {
            throw new IllegalStateException(ex);
        }
    }

    public EnumPropertyMapping mapEnums() {
        Validate.isTrue(firstEnumMap.size() == secondEnumMap.size());
        Validate.isTrue(Sets.difference(firstEnumMap.keySet(), secondEnumMap.keySet()).isEmpty());

        Map<Object, Object> mapA = new HashMap<>();
        Map<Object, Object> mapB = new HashMap<>();

        for (Map.Entry<Object, Object> obj : firstEnumMap.entrySet()) {
            Object secondMapVal = secondEnumMap.get(obj.getKey());
            mapA.put(obj.getValue(), secondMapVal);
            mapB.put(secondMapVal, obj.getValue());
        }
        return new EnumPropertyMapping(mapA, mapB, firstEnumType, secondEnumType);
    }
}

}

-- TEST - 测试

import org.junit.Test;

import com.bondarenko.common.utils.lang.enums.FirstEnum;
import com.bondarenko.common.utils.lang.enums.SecondEnum;

import static junit.framework.TestCase.assertEquals;

public class EnumPropertyMappingTest {

@Test
public void testGetMappedEnum() {
    EnumPropertyMapping mapping = EnumPropertyMapping.builder()
                                                                                                     .addSecond(SecondEnum.class, "valueId")
                                                                                                     .addFirst(FirstEnum.class, "value")
                                                                                                     .mapEnums();

    assertEquals(SecondEnum.D, mapping.getCorrespondingEnum(FirstEnum.B));
    assertEquals(FirstEnum.A, mapping.getCorrespondingEnum(SecondEnum.C));
}

}

Guava's Enums.getIfPresent() on Enum.name() Enums.getIfPresent()上的Guava的Enum.name()

Our case was a particular specialization of this one. 我们的案例是这个案例的特殊专长。 We do have two Enum : one we use in the application and another one we use in the core library. 我们有两个Enum :一个是我们在应用程序中使用的,另一个是我们在核心库中使用的。 The core library is used by a handful of applications, by different teams. 核心库由不同团队的少数应用程序使用。 Each application views a subset of the whole functionality. 每个应用程序都查看整个功能的子集。 The whole functionality is configured with the enums in order to switch on and off, throttle up or down, select strategies, etc. 整个功能配置为枚举,以打开和关闭,加油或关闭,选择策略等。

So we ended up with: 所以我们最终得到:

  1. one enum for the library, containing all the possible configurations, visible from the applications and also some library-specific 库的一个枚举,包含所有可能的配置,从应用程序可见,也可以是一些特定于库的
  2. one enum for each application, containing the literals corresponding to what the application can see/touch in the library, and some application-specific 每个应用程序的一个枚举,包含与应用程序在库中可以看到/触摸的内容相对应的文字,以及一些特定于应用程序的文字

Then as we pass data down to the library, we adapt all data and also those configurations. 然后,当我们将数据传递到库时,我们会调整所有数据以及这些配置。 We own all enums, so we can choose to call the same configuration with the same literal in different enums. 我们拥有所有枚举,因此我们可以选择在不同的枚举中使用相同的文字调用相同的配置。

Enum LibraryConfig {
    FUNCTION_ONE,
    FUNCTION_TWO,
    FUNCTION_THREE,
    FUNCTION_FOUR;
}

Enum Aplication1Config {
    FUNCTION_ONE,
    FUNCTION_TWO,
    FUNCTION_THREE,
    APPL1_FUNCTION_ONE,
    APPL1_FUNCTION_TWO;
}

Enum Aplication2Config {
    FUNCTION_ONE,
    FUNCTION_TWO,
    FUNCTION_FOUR;
    APPL2_FUNCTION_ONE;
}

When we need to convert from one type to another (app --> lib or lib --> app) we use the getIfPresent() method from com.google.common.base.Enums in this way: 当我们需要从一种类型转换为另一种类型(app - > lib或lib - > app)时,我们以这种方式使用com.google.common.base.Enums中的getIfPresent()方法:

Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = Enums.getIfPresent(LibraryConfig.class, config1App1.name()).orNull();

We check configLib for null value to see if there was successful conversion. 我们检查configLib是否为null值,看看是否有成功的转换。 This last step we use because of the APPX_FUNCTION_YYY, which are application-specific, and for the conversion on the direction lib --> app, not to pass configuration values library-specific ( FUNCTION_FOUR in the example). 我们使用的最后一步是APPX_FUNCTION_YYY,它们是特定于应用程序的, 并且用于方向lib - > app的转换,而不是传递特定于库的配置值(示例中为FUNCTION_FOUR )。

maven's dependency management: maven的依赖管理:

Just in case anyone needs it: 万一有人需要它:

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>20.0</version>
    </dependency>

Home grown version: 自家种植的版本:

You can make your own conversion using the Enum methods, but you have to take care of the exception to detect when the conversion did not succeed: 您可以使用Enum方法进行自己的转换,但是您必须处理异常以检测转换何时未成功:

try {
    Aplication1Config config1App1 = FUNCTION_TWO;
    LibraryConfig configLib = LibraryConfig.valueOf(config1App1.name());
} catch (IllegalArgumentException iae) {
    // if the conversion did not succeed
}

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

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