【问题标题】:How to convert one enum to another enum in java?如何在java中将一个枚举转换为另一个枚举?
【发布时间】:2012-06-18 05:43:23
【问题描述】:

我有;

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

并需要将其转换为以下内容;

public enum Simple {
    DONE, RUNNING, ERROR;
}

所以首先PASSED->DONEINPROCESS->RUNNING,但所有错误都应该是:ERROR。显然可以为所有值编写案例,但可能有更好的解决方案?

【问题讨论】:

    标签: java enums


    【解决方案1】:

    就我个人而言,我只会创建一个 Map<Detailed, Simple> 并明确地执行它 - 甚至可能使用 switch 语句。

    另一种选择是将映射传递给构造函数 - 当然,您只能这样做:

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

    (我发现这比 Ted 使用多态的方法更简单,因为我们并没有真正尝试提供不同的行为 - 只是不同的简单映射。)

    虽然您可以潜在地用序数值做一些狡猾的事情,但它会不那么明显,并且需要更多的代码 - 我认为没有任何好处。

    【讨论】:

    • 这是更干净的 IMO,现在如果您需要在多个地方进行映射,Enum 本身会提供它。
    【解决方案2】:

    一种方法是在 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
        }
    }
    

    然后,您可以在想要进行映射时简单地调用该方法:

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

    它的优点是将映射知识与Detailed 枚举(可能属于它的位置)放在一起。它的缺点是将Simple 的知识与Detailed 的代码混合在一起。这可能是坏事,也可能不是坏事,具体取决于您的系统架构。

    【讨论】:

    • 我认为将一个枚举中的映射逻辑放到另一个枚举中会破坏封装,引入耦合并降低内聚性。枚举的关注点应该只是表示一组状态(或概念)。从一个枚举映射到另一个枚举的问题应该单独封装。
    • @Chomeh - 我在回答中确实注意到Detailed 最终需要了解Simple。 (基本上,我表达了与您在评论中所做的相同的担忧,但对称其为“缺点”的批评相当模糊。您说得更准确。)不过,我认为这种方法值得考虑。 OP 的枚举名称表明Detailed 在概念上是Simple 的改进。如果是这样的话,那么 Detailed 耦合到 Simple 一点也不坏(就像子类耦合到它的父类并不总是坏事)。
    【解决方案3】:

    使用EnumMap

    我通过实现转换服务将外部 xml 接口与内部域模型分离。这包括将枚举从 jaxb 生成的代码映射到域模型枚举。

    使用静态 EnumMap 将转换的关注点封装在负责转换的类中。它的凝聚力。

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

    【讨论】:

      【解决方案4】:

      Guava 在Enum.name() 上的Enums.getIfPresent()

      我们的案例是这个案例的一个特殊专业。我们确实有两个Enum:一个在应用程序中使用,另一个在核心库中使用。核心库被不同团队的少数应用程序使用。每个应用程序都查看整个功能的一个子集。整个功能都使用枚举进行配置,以便打开和关闭、加速或减速、选择策略等。

      所以我们最终得到:

      1. 库的一个枚举,包含所有可能的配置,从应用程序可见,还有一些特定于库的
      2. 每个应用程序一个枚举,包含与应用程序在库中可以看到/触摸的内容相对应的文字,以及一些特定于应用程序的内容

      然后,当我们将数据向下传递到库时,我们会调整所有数据以及这些配置。我们拥有所有枚举,因此我们可以选择在不同的枚举中使用相同的文字调用相同的配置。

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

      当我们需要从一种类型转换为另一种类型时(app --> lib 或 lib --> app),我们使用com.google.common.base.Enums 中的getIfPresent() 方法:

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

      我们检查configLibnull 值以查看是否成功转换。我们使用最后一步是因为 APPX_FUNCTION_YYY,它是特定于应用程序的,用于方向 lib --> 应用程序上的转换,而不是传递特定于库的配置值(FUNCTION_FOUR 在示例)。

      maven的依赖管理:

      以防万一有人需要:

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

      本土版:

      您可以使用 Enum 方法进行自己的转换,但您必须注意异常以检测何时转换不成功:

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

      【讨论】:

        【解决方案5】:

        Ted 的回答很Java,但是表达方式

        passed == PASSED ? DONE : ERROR
        

        也能胜任。

        【讨论】:

        • 地图的 INPROCESS -> RUNNING 部分发生了什么?
        • @Ted:您当然是对的,感谢您的纠正。我不会提议PASSED ? DONE : INPROCESS ? RUNNING : ERROR,因为这显然不太可读。 (话又说回来,我以前见过,而且格式很好,它does not look too strange...)
        • @manuelvigarcia - 什么链接?还是应该将您的评论发送给 tiwo?
        • @tiwo 链接无效。您可能想找到另一个展示良好格式的网站...我对这个解决方案很感兴趣
        【解决方案6】:

        对我来说,这听起来更像是一个概念问题而不是编程问题。为什么不直接删除“Simple”枚举类型并在程序的所有位置使用另一种枚举类型?

        通过另一个示例更清楚地说明这一点:您真的会尝试为一周中的工作日(周一至周五)定义一个枚举类型,并为一周中的所有天(周一至周日)定义另一个枚举吗?

        【讨论】:

        • 由于 enum 类型不能从 Java 中的其他 enum 类型继承,因此您几乎会不时地进行这种映射。
        • 我可以想到一个应用程序--administration-- 您希望第二天是星期一--下一个银行工作日-- 在某些情况下和星期六--下一个“自然”日--其他。例如:处理出纳员注意的约会需要银行工作日,但在计算假期时,您会计算自然日。一旦建立起来,需要一段时间来调整它们并不是不可想象的。
        【解决方案7】:

        这是带有测试的简单枚举映射器:

        -- 实现

        -- 枚举

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

        --映射器

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

        -- 测试

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-11-14
          • 2015-04-06
          • 1970-01-01
          • 2013-12-13
          • 1970-01-01
          • 1970-01-01
          • 2010-12-21
          • 1970-01-01
          相关资源
          最近更新 更多