Подтвердить что ты не робот

Как преобразовать один enum в другой enum в java?

У меня есть:

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

и нужно преобразовать его в следующее:

public enum Simple {
    DONE, RUNNING, ERROR;
}

Итак, сначала PASSEDDONE и INPROCESSRUNNING, но все ошибки должны быть: ERROR. Очевидно, что можно писать случаи для всех значений, но может быть лучшее решение?

4b9b3361

Ответ 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: один, который мы используем в приложении, и другой, который мы используем в основной библиотеке. Базовая библиотека используется несколькими приложениями, различными командами. Каждое приложение просматривает подмножество всей функциональности. Вся функциональность настраивается с помощью перечислений, чтобы включать и выключать, регулировать скорость вверх или вниз, выбирать стратегии и т.д.

Итак, мы закончили с:

  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), мы используем метод 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
}