3

I have two enum classes one is external and the other is internal.

public enum ExternalEnum
{
  EXTERNAL_CAR, EXTERNAL_VAN, EXTERNAL_BUS
}

public enum InternalEnum
{
  CAR, VAN, BUS
}

I need to map internal to external and external to internal. So I have done the following,

public class EnumMapper
{
  public ExternalEnum toExternal(InternalEnum internalEnum)
  {
    switch (internalEnum)
    {
    case BUS:
      return ExternalEnum.EXTERNAL_BUS;
    case CAR:
      return ExternalEnum.EXTERNAL_CAR;
    case VAN:
      return ExternalEnum.EXTERNAL_VAN;
    }
    return null;
  }

  public InternalEnum toInternal(ExternalEnum externalEnum)
  {
    switch (externalEnum)
    {
    case EXTERNAL_BUS:
      return InternalEnum.BUS;
    case EXTERNAL_CAR:
      return InternalEnum.CAR;
    case EXTERNAL_VAN:
      return InternalEnum.VAN;
    }
    return null;
  }
}

So same kind of mapping is repeating in two methods. Isn't there a good approach to achieve this using less amount of code?

Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
abo
  • 378
  • 4
  • 19

3 Answers3

3

tl;dr

InternalEnum.values()[ externalEnum.ordinal() ]

Details

Assuming the equivalent enum objects are declared in the same order, we can retrieve one enum object from the other by accessing the array of all enum objects returned from a call to the implicit public static T[] values() method (see this Question) and access by zero-based index number retrieved from the misnamed Enum#ordinal method.

For example, InternalEnum.values() returns an array of all the enum objects in the order in which they were declared. Then we use array accessor […] to retrieve the object matching the index number of its mate in the other enum.

InternalEnum internalEnum = InternalEnum.values()[ externalEnum.ordinal() ] ;

And vice versa:

ExternalEnum externalEnum = ExternalEnum.values()[ internalEnum.ordinal() ] ;

Here is example usage. Bonus tip: In Java 16+, we can declare an enum locally — see JEP 395: Records.

enum ExternalEnum { EXTERNAL_CAR, EXTERNAL_VAN, EXTERNAL_BUS }
enum InternalEnum { CAR, VAN, BUS }

System.out.println(
        InternalEnum.values()[ ExternalEnum.EXTERNAL_VAN.ordinal() ]
);

System.out.println(
        ExternalEnum.values()[ InternalEnum.VAN.ordinal() ]
);

VAN

EXTERNAL_VAN

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

The answer from Basil Bourque is good, but I do not like to use ordinal() because you can easily rotate the enum constants and business logic will be broken.

You did almost correct, just remember, that enum constant is a plain old java class instance and it can be expanded with additional functionality. Therefore it's better to hide all mapper logic inside an enum.

public enum ExternalEnum {
    EXTERNAL_CAR,
    EXTERNAL_VAN,
    EXTERNAL_BUS
}

public enum InternalEnum {
    CAR(ExternalEnum.EXTERNAL_CAR),
    VAN(ExternalEnum.EXTERNAL_VAN),
    BUS(ExternalEnum.EXTERNAL_BUS);

    private final ExternalEnum externalEnum;

    InternalEnum(ExternalEnum externalEnum) {
        this.externalEnum = externalEnum;
    }

    public ExternalEnum toExternal() {
        return externalEnum;
    }

    public static Optional<InternalEnum> parseExternal(ExternalEnum externalEnum) {
        for (InternalEnum internalEnum : values())
            if (internalEnum.externalEnum == externalEnum)
                return Optional.of(internalEnum);
        return Optional.empty();
    }
}

Demo

ExternalEnum externalEnum = ExternalEnum.EXTERNAL_CAR;
Optional<InternalEnum> optInternalEnum = InternalEnum.parseExternal(externalEnum);

System.out.println(externalEnum);
System.out.println(optInternalEnum.orElse(null));
System.out.println(optInternalEnum.map(InternalEnum::toExternal).orElse(null));

An alternative is to have only a Mapper

public final class EnumMapper {
    
    private final Map<ExternalEnum, InternalEnum> MAP_EXT_INT = Map.of(
            ExternalEnum.EXTERNAL_CAR, InternalEnum.CAR,
            ExternalEnum.EXTERNAL_VAN, InternalEnum.VAN,
            ExternalEnum.EXTERNAL_BUS, InternalEnum.BUS);
            
    private final Map<InternalEnum, ExternalEnum> MAP_INT_EXT = Map.of(
            InternalEnum.CAR, ExternalEnum.EXTERNAL_CAR,
            InternalEnum.VAN, ExternalEnum.EXTERNAL_VAN,
            InternalEnum.BUS, ExternalEnum.EXTERNAL_BUS);
            
    public static ExternalEnum toExternal(InternalEnum internalEnum) {
        return MAP_INT_EXT.get(internalEnum);
    }
    
    public InternalEnum toInternal(ExternalEnum externalEnum) {
        return MAP_EXT_INT.get(externalEnum);
    }
    
    private EnumMapper() {}
}

public enum ExternalEnum {
    EXTERNAL_CAR,
    EXTERNAL_VAN,
    EXTERNAL_BUS
}

public enum InternalEnum {
    CAR,
    VAN,
    BUS;
}
Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
1

I was able to achieve this by using BiMap with less amount of code.

import com.google.common.collect.EnumBiMap;

public class EnumBiMapMapper
{
  private EnumBiMap<InternalEnum, ExternalEnum> enumEnumBiMap = EnumBiMap.create(InternalEnum.class, ExternalEnum.class);

  public EnumBiMapMapper()
  {
    enumEnumBiMap.put(InternalEnum.BUS, ExternalEnum.EXTERNAL_BUS);
    enumEnumBiMap.put(InternalEnum.CAR, ExternalEnum.EXTERNAL_CAR);
    enumEnumBiMap.put(InternalEnum.VAN, ExternalEnum.EXTERNAL_VAN);
  }

  public ExternalEnum toExternal(InternalEnum internalEnum)
  {
    return enumEnumBiMap.get(internalEnum);
  }

  public InternalEnum toInternal(ExternalEnum externalEnum)
  {
    return enumEnumBiMap.inverse().get(externalEnum);
  }
}
abo
  • 378
  • 4
  • 19