Как преобразовать один enum в другой enum в java?
У меня есть:
public enum Detailed {
PASSED, INPROCESS, ERROR1, ERROR2, ERROR3;
}
и нужно преобразовать его в следующее:
public enum Simple {
DONE, RUNNING, ERROR;
}
Итак, сначала PASSED
→ DONE
и INPROCESS
→ RUNNING
, но все ошибки должны быть: ERROR
. Очевидно, что можно писать случаи для всех значений, но может быть лучшее решение?
Ответы
Ответ 1
Одним из способов является определение метода asSimple()
в вашем Detailed
перечислении:
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
. Это может или не может быть плохо, в зависимости от архитектуры вашей системы.
Ответ 2
Лично я бы просто создал 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;
}
}
(я нахожу это более простым, чем подход Теда, использующего полиморфизм, поскольку мы на самом деле не пытаемся обеспечить другое поведение - просто другое простое сопоставление.)
В то время как вы могли бы сделать что-то хитрое с порядковым значением, было бы гораздо менее очевидным и взять больше кода - я не думаю, что будет какая-то польза.
Ответ 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
Тед-ответ очень Javaly, но выражение
passed == PASSED ? DONE : ERROR
тоже выполнит эту работу.
Ответ 5
Для меня это больше похоже на концептуальную проблему, чем на проблему программирования. Почему бы вам просто не удалить "простой" тип перечисления и использовать его во всех местах программы?
Просто, чтобы сделать это более понятным с помощью другого примера: действительно ли вы попытаетесь определить тип перечисления для рабочих дней в неделю (с понедельника по пятницу) и другое перечисление для всех дней недели (с понедельника по воскресенье)?
Ответ 6
Вот простой транслятор перечисления с тестом:
- ОСУЩЕСТВЛЕНИЕ
- 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
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));
}
}
Ответ 7
Guava Enums.getIfPresent()
для Enum.name()
Наш случай был особой специализацией этого. У нас есть два Enum
: один, который мы используем в приложении, и другой, который мы используем в основной библиотеке. Базовая библиотека используется несколькими приложениями, различными командами. Каждое приложение просматривает подмножество всей функциональности. Вся функциональность настраивается с помощью перечислений, чтобы включать и выключать, регулировать скорость вверх или вниз, выбирать стратегии и т.д.
Итак, мы закончили с:
- одно перечисление для библиотеки, содержащее все возможные конфигурации, видимые из приложений, а также некоторые специфичные для библиотеки
- одно перечисление для каждого приложения, содержащее литералы, соответствующие тому, что приложение может видеть/трогать в библиотеке, и некоторые специфичные для приложения
Затем, когда мы передаем данные в библиотеку, мы адаптируем все данные, а также эти конфигурации. Мы владеем всеми перечислениями, поэтому мы можем вызывать одну и ту же конфигурацию с одним и тем же литералом в разных перечислениях.
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), мы используем метод getIfPresent()
из com.google.common.base.Enums
следующим образом:
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = Enums.getIfPresent(LibraryConfig.class, config1App1.name()).orNull();
Мы проверяем configLib
на null
значение, чтобы увидеть, было ли успешное преобразование. Этот последний шаг мы используем из-за APPX_FUNCTION_YYY, которые являются специфическими для приложения, и для преобразования в направлении lib → app, чтобы не передавать значения конфигурации, специфичные для библиотеки (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
}