在 java 中将枚举映射到另一个代码量较少的枚举

Map enum to another enum with less amount of code in java

我有两个枚举类一个是外部的,另一个是内部的。

public enum ExternalEnum
{
  EXTERNAL_CAR, EXTERNAL_VAN, EXTERNAL_BUS
}

public enum InternalEnum
{
  CAR, VAN, BUS
}

我需要将内部映射到外部,将外部映射到内部。所以我做了以下,

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

所以同一种映射在两种方法中重复出现。有没有一种好的方法可以使用更少的代码来实现这一点?

tl;博士

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

详情

假设以相同的顺序声明等效的枚举对象,我们可以通过访问从调用隐式 public static T[] values() 方法返回的所有枚举对象的数组来从另一个枚举对象中检索一个枚举对象(参见 this Question) and access by zero-based index number retrieved from the misnamed Enum#ordinal方法。

例如,InternalEnum.values() returns 所有枚举对象按声明顺序排列的数组。然后我们使用数组访问器 […] 来检索匹配其伙伴在另一个枚举中的索引号的对象。

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

反之亦然:

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

这是用法示例。额外提示:在 Java 16+ 中,我们可以在本地声明一个枚举——参见 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 的回答很好,但我不喜欢使用 ordinal() 因为你可以很容易地轮换 enum 常量和业务逻辑会坏掉的。

您几乎是正确的,请记住,enum 常量是一个普通的旧 java class 实例,它可以通过附加功能进行扩展。因此,最好将所有映射器逻辑隐藏在 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();
    }
}

演示

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

另一种方法是只使用 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;
}

我能够通过使用 BiMap 以更少的代码实现这一点。

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