J'ai;
public enum Detailed {
PASSED, INPROCESS, ERROR1, ERROR2, ERROR3;
}
et besoin de le convertir au suivant;
public enum Simple {
DONE, RUNNING, ERROR;
}
Donc d'abord PASSED
-> DONE
et INPROCESS
-> RUNNING
, mais toutes les erreurs devraient être: ERROR
. De toute évidence, il est possible d'écrire des observations pour toutes les valeurs, mais il pourrait y avoir une meilleure solution?
Une solution consiste à définir une méthode asSimple()
dans votre Detailed
enum:
public enum Detailed {
PASSED {
@Override
Simple asSimple() {
return PASSED;
}
},
INPROCESS {
@Override
Simple asSimple() {
return RUNNING;
}
},
ERROR1,
ERROR2,
ERROR3;
public Simple asSimple() {
return Simple.ERROR; // default mapping
}
}
Vous pouvez ensuite simplement appeler la méthode lorsque vous souhaitez effectuer le mappage:
Detailed code = . . .
Simple simpleCode = code.asSimple();
Il a l'avantage de mettre la connaissance de la cartographie avec l'enumération Detailed
(où elle appartient peut-être). Il a pour inconvénient d’avoir la connaissance de Simple
mélangée au code de Detailed
. Cela peut ou peut ne pas être une mauvaise chose, selon l'architecture de votre système.
Personnellement, je créerais simplement un Map<Detailed, Simple>
et le ferais explicitement - ou même utiliser potentiellement une instruction switch
.
Une autre alternative serait de passer le mappage au constructeur - vous ne pouvez le faire que dans un sens, bien sûr:
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;
}
}
(Je trouve cela plus simple que l'approche de Ted consistant à utiliser le polymorphisme, car nous n'essayons pas vraiment de fournir un comportement différent behavior - juste un mappage simple différent.)
Tandis que vous pourriez potentiellement faire quelque chose de rusé avec la valeur ordinale, ce serait beaucoup moins évident, et prendre plus de code - je ne pense pas qu'il y aurait aucun avantage.
Utilisez EnumMap
Je dissocie mon interface XML externe de mon modèle de domaine interne en implémentant un service de transformation. Cela inclut la correspondance des énumérations du code généré par jaxb avec les énumérations de modèle de domaine.
L'utilisation d'un objet EnumMap statique résume le problème de la transformation dans la classe responsable de la transformation. Sa cohésion.
@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);
}
...
}
La réponse de Ted est très Javaly, mais l'expression
passed == PASSED ? DONE : ERROR
ferait le travail aussi.
Pour moi, cela ressemble plus à un problème conceptuel qu’à un problème de programmation. Pourquoi ne vous contentez-vous pas de supprimer le type "Simple" et d'utiliser l'autre à la place du programme?
Pour clarifier cela avec un autre exemple: Voulez-vous vraiment définir un type d’énum pour les jours de travail de la semaine (du lundi au vendredi) et un autre enum pour tous les jours de la semaine (du lundi au dimanche)?
Enums.getIfPresent()
de Guava sur Enum.name()
Notre cas était une spécialisation particulière de celui-ci. Nous avons deux Enum
: un que nous utilisons dans l’application et un autre que nous utilisons dans la bibliothèque principale. La bibliothèque principale est utilisée par une poignée d'applications, par différentes équipes. Chaque application affiche un sous-ensemble de toutes les fonctionnalités. Toute la fonctionnalité est configurée avec les énumérations afin d’allumer et d’éteindre, d’accélérer ou de réduire les vitesses, de sélectionner des stratégies, etc.
Nous nous sommes donc retrouvés avec:
Ensuite, lorsque nous transmettons des données à la bibliothèque, nous adaptons toutes les données et également ces configurations. Nous possédons tous les enums, nous pouvons donc choisir d'appeler la même configuration avec le même littéral dans des enums différents.
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;
}
Lorsque nous devons convertir un type en un autre (app -> lib ou lib -> app), nous utilisons la méthode getIfPresent()
de com.google.common.base.Enums
de cette façon:
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = Enums.getIfPresent(LibraryConfig.class, config1App1.name()).orNull();
Nous vérifions configLib
pour null
valeur pour voir si la conversion a réussi. Cette dernière étape est utilisée pour APPX_FUNCTION_YYY, qui sont spécifiques à l'application, et pour la conversion dans le sens lib -> app, afin de ne pas transmettre les valeurs de configuration spécifiques à la bibliothèque (FUNCTION_FOUR
dans l'exemple).
Juste au cas où quelqu'un en aurait besoin:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
Vous pouvez effectuer votre propre conversion à l'aide des méthodes Enum, mais vous devez vous occuper de l'exception pour détecter le moment où la conversion a échoué:
try {
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = LibraryConfig.valueOf(config1App1.name());
} catch (IllegalArgumentException iae) {
// if the conversion did not succeed
}
Voici le mappeur enum simple avec test:
-- LA MISE EN OEUVRE
- 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);
}
}
}
- 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));
}
}