方便地在enum和int / String之间映射

时间:2011-02-16 19:36:08

标签: java enums coding-style

当处理只能获取有限数量值的变量/参数时,我尝试始终使用Java enum,如

public enum BonusType {
  MONTHLY, YEARLY, ONE_OFF
}

只要我留在我的代码中,就可以了。但是,为了相同的目的,我经常需要与使用普通int(或String)值的其他代码进行交互,或者我需要从/向存储数据的数据库进行读/写操作数字或字符串。

在这种情况下,我想有一种方便的方法将每个枚举值与一个整数相关联,这样我就可以双向转换(换句话说,我需要一个“可逆枚举”)。

从enum到int很容易:

public enum BonusType {
  public final int id;

  BonusType(int id) {
    this.id = id;
  }
  MONTHLY(1), YEARLY(2), ONE_OFF(3);
}

然后我可以将{int}值作为BonusType x = MONTHLY; int id = x.id;

然而,我看不出反面的好方法,即从int到enum。理想情况下,像

BonusType bt = BonusType.getById(2); 

我能提出的唯一解决方案是:

  • 将查找方法放入枚举中,使用BonusType.values()填充地图“int - > enum”,然后缓存并将其用于查找。会工作,但我必须将这个方法完全复制到我使用的每个枚举中: - (。
  • 将查找方法放入静态实用程序类中。然后我只需要一个“查找”方法,但是我必须使用反射来使其适用于任意枚举。

对于这样一个简单的(?)问题,这两种方法看起来都非常尴尬。

还有其他想法/见解吗?

17 个答案:

答案 0 :(得分:321)

enum→int

yourEnum.ordinal()

int→enum

EnumType.values()[someInt]

String→enum

EnumType.valueOf(yourString)

enum→String

yourEnum.name()

附注:
正如您正确指出的那样,ordinal()从版本到版本可能会“不稳定”。这就是为什么我总是将常量存储为数据库中的字符串的确切原因。 (实际上,当使用MySql时,我将它们存储为MySql enums!)

答案 1 :(得分:36)

http://www.javaspecialists.co.za/archive/Issue113.html

解决方案与您的解决方案类似,使用int值作为枚举定义的一部分。然后,他继续创建基于泛型的查找实用程序:

public class ReverseEnumMap<V extends Enum<V> & EnumConverter> {
    private Map<Byte, V> map = new HashMap<Byte, V>();
    public ReverseEnumMap(Class<V> valueType) {
        for (V v : valueType.getEnumConstants()) {
            map.put(v.convert(), v);
        }
    }

    public V get(byte num) {
        return map.get(num);
    }
}

这个解决方案很好,并且不需要“摆弄反射”,因为它基于所有枚举类型隐式继承Enum接口的事实。

答案 2 :(得分:27)

我在网上发现了这个,它非常有用且易于实现。 这个解决方案不是由我做的

http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks

public enum Status {
 WAITING(0),
 READY(1),
 SKIPPED(-1),
 COMPLETED(5);

 private static final Map<Integer,Status> lookup 
      = new HashMap<Integer,Status>();

 static {
      for(Status s : EnumSet.allOf(Status.class))
           lookup.put(s.getCode(), s);
 }

 private int code;

 private Status(int code) {
      this.code = code;
 }

 public int getCode() { return code; }

 public static Status get(int code) { 
      return lookup.get(code); 
 }

}

答案 3 :(得分:6)

似乎这个问题的答案已经过时了Java 8的发布。

  1. 不要使用序数,因为如果持久存在,序数是不稳定的 JVM,如数据库。
  2. 创建静态地图相对容易 关键值。
  3. public enum AccessLevel {
      PRIVATE("private", 0),
      PUBLIC("public", 1),
      DEFAULT("default", 2);
    
      AccessLevel(final String name, final int value) {
        this.name = name;
        this.value = value;
      }
    
      private final String name;
      private final int value;
    
      public String getName() {
        return name;
      }
    
      public int getValue() {
        return value;
      }
    
      static final Map<String, AccessLevel> names = Arrays.stream(AccessLevel.values())
          .collect(Collectors.toMap(AccessLevel::getName, Function.identity()));
      static final Map<Integer, AccessLevel> values = Arrays.stream(AccessLevel.values())
          .collect(Collectors.toMap(AccessLevel::getValue, Function.identity()));
    
      public static AccessLevel fromName(final String name) {
        return names.get(name);
      }
    
      public static AccessLevel fromValue(final int value) {
        return values.get(value);
      }
    }
    

答案 4 :(得分:4)

org.apache.commons.lang.enums.ValuedEnum;

为了节省我为每个枚举编写大量样板代码或复制代码,我使用了Apache Commons Lang ValuedEnum

<强>定义

public class NRPEPacketType extends ValuedEnum {    
    public static final NRPEPacketType TYPE_QUERY = new NRPEPacketType( "TYPE_QUERY", 1);
    public static final NRPEPacketType TYPE_RESPONSE = new NRPEPacketType( "TYPE_RESPONSE", 2);

    protected NRPEPacketType(String name, int value) {
        super(name, value);
    }
}

<强>用法:

int - &gt; ValuedEnum:

NRPEPacketType packetType = 
 (NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);

答案 5 :(得分:3)

您可以使用类似

的内容
interface EnumWithId {
    public int getId();

}


enum Foo implements EnumWithId {

   ...
}

这样可以减少实用程序类中反射的需要。

答案 6 :(得分:3)

在此代码中,对于永久性和强烈搜索,请使用内存或进程,并选择内存,将转换器数组作为索引。 我希望它有用

public enum Test{ 
VALUE_ONE(101, "Im value one"),
VALUE_TWO(215, "Im value two");
private final int number;
private final byte[] desc;

private final static int[] converter = new int[216];
static{
    Test[] st = values();
    for(int i=0;i<st.length;i++){
        cv[st[i].number]=i;
    }
}

Test(int value, byte[] description) {
    this.number = value;
    this.desc = description;
}   
public int value() {
    return this.number;
}
public byte[] description(){
    return this.desc;
}

public static String description(int value) {
    return values()[converter[rps]].desc;
}

public static Test fromValue(int value){
return values()[converter[rps]];
}
}

答案 7 :(得分:2)

使用界面向其展示谁是老板。

public interface SleskeEnum {
    int id();

    SleskeEnum[] getValues();

}

public enum BonusType implements SleskeEnum {


  MONTHLY(1), YEARLY(2), ONE_OFF(3);

  public final int id;

  BonusType(int id) {
    this.id = id;
  }

  public SleskeEnum[] getValues() {
    return values();
  }

  public int id() { return id; }


}

public class Utils {

  public static SleskeEnum getById(SleskeEnum type, int id) {
      for(SleskeEnum t : type.getValues())
          if(t.id() == id) return t;
      throw new IllegalArgumentException("BonusType does not accept id " + id);
  }

  public static void main(String[] args) {

      BonusType shouldBeMonthly = (BonusType)getById(BonusType.MONTHLY,1);
      System.out.println(shouldBeMonthly == BonusType.MONTHLY);

      BonusType shouldBeMonthly2 = (BonusType)getById(BonusType.MONTHLY,1);
      System.out.println(shouldBeMonthly2 == BonusType.YEARLY);

      BonusType shouldBeYearly = (BonusType)getById(BonusType.MONTHLY,2);
      System.out.println(shouldBeYearly  == BonusType.YEARLY);

      BonusType shouldBeOneOff = (BonusType)getById(BonusType.MONTHLY,3);
      System.out.println(shouldBeOneOff == BonusType.ONE_OFF);

      BonusType shouldException = (BonusType)getById(BonusType.MONTHLY,4);
  }
}

结果:

C:\Documents and Settings\user\My Documents>java Utils
true
false
true
true
Exception in thread "main" java.lang.IllegalArgumentException: BonusType does not accept id 4
        at Utils.getById(Utils.java:6)
        at Utils.main(Utils.java:23)

C:\Documents and Settings\user\My Documents>

答案 8 :(得分:2)

  

反向枚举的非常简洁的用法示例

第1步 定义interface EnumConverter

public interface EnumConverter <E extends Enum<E> & EnumConverter<E>> {
    public String convert();
    E convert(String pKey);
}

第2步

创建一个类名ReverseEnumMap

import java.util.HashMap;
import java.util.Map;

public class ReverseEnumMap<V extends Enum<V> & EnumConverter<V>> {
    private Map<String, V> map = new HashMap<String, V>();

    public ReverseEnumMap(Class<V> valueType) {
        for (V v : valueType.getEnumConstants()) {
            map.put(v.convert(), v);
        }
    }

    public V get(String pKey) {
        return map.get(pKey);
    }
}

第3步

使用Enum转到implement课程和EnumConverter<ContentType>,然后覆盖界面方法。您还需要初始化静态ReverseEnumMap。

public enum ContentType implements EnumConverter<ContentType> {
    VIDEO("Video"), GAME("Game"), TEST("Test"), IMAGE("Image");

    private static ReverseEnumMap<ContentType> map = new ReverseEnumMap<ContentType>(ContentType.class);

    private final String mName;

    ContentType(String pName) {
        this.mName = pName;
    }

    String value() {
        return this.mName;
    }

    @Override
    public String convert() {
        return this.mName;
    }

    @Override
    public ContentType convert(String pKey) {
        return map.get(pKey);
    }
}

第4步

现在创建一个Communication类文件并调用它的新方法将Enum转换为StringString转换为Enum。我刚刚把主要方法用于解释目的。

public class Communication<E extends Enum<E> & EnumConverter<E>> {
    private final E enumSample;

    public Communication(E enumSample) {
        this.enumSample = enumSample;
    }

    public String resolveEnumToStringValue(E e) {
        return e.convert();
    }

    public E resolveStringEnumConstant(String pName) {
        return enumSample.convert(pName);
    }

//Should not put main method here... just for explanation purpose. 
    public static void main(String... are) {
        Communication<ContentType> comm = new Communication<ContentType>(ContentType.GAME);
        comm.resolveEnumToStringValue(ContentType.GAME); //return Game
        comm.resolveStringEnumConstant("Game"); //return GAME (Enum)
    }
}

Click for for complete explanation

答案 9 :(得分:2)

.ordinal()values()[i]都不稳定,因为它们依赖于枚举的顺序。因此,如果您更改枚举的顺序或添加/删除某些程序将会中断。

这是一个在enum和int之间进行映射的简单而有效的方法。

public enum Action {
    ROTATE_RIGHT(0), ROTATE_LEFT(1), RIGHT(2), LEFT(3), UP(4), DOWN(5);

    public final int id;
    Action(int id) {
        this.id = id;
    }

    public static Action get(int id){
        for (Action a: Action.values()) {
            if (a.id == id)
                return a;
        }
        throw new IllegalArgumentException("Invalid id");
    }
}

将它应用于字符串应该不难。

答案 10 :(得分:1)

为了完整起见,这里是一种通过索引从任何枚举类型中检索枚举值的通用方法。我的目的是使方法看起来像Enum.valueOf(Class, String)。 Fyi,我从here复制了这个方法。

指数相关问题(此处已深入讨论)仍然适用。

/**
 * Returns the {@link Enum} instance for a given ordinal.
 * This method is the index based alternative
 * to {@link Enum#valueOf(Class, String)}, which
 * requires the name of an instance.
 * 
 * @param <E> the enum type
 * @param type the enum class object
 * @param ordinal the index of the enum instance
 * @throws IndexOutOfBoundsException if ordinal < 0 || ordinal >= enums.length
 * @return the enum instance with the given ordinal
 */
public static <E extends Enum<E>> E valueOf(Class<E> type, int ordinal) {
    Preconditions.checkNotNull(type, "Type");
    final E[] enums = type.getEnumConstants();
    Preconditions.checkElementIndex(ordinal, enums.length, "ordinal");
    return enums[ordinal];
}

答案 11 :(得分:1)

非常好的问题:-)我之前使用的解决方案类似于Mr.Ferguson。我们的反编译枚举看起来像这样:

final class BonusType extends Enum
{

    private BonusType(String s, int i, int id)
    {
        super(s, i);
        this.id = id;
    }

    public static BonusType[] values()
    {
        BonusType abonustype[];
        int i;
        BonusType abonustype1[];
        System.arraycopy(abonustype = ENUM$VALUES, 0, abonustype1 = new BonusType[i = abonustype.length], 0, i);
        return abonustype1;
    }

    public static BonusType valueOf(String s)
    {
        return (BonusType)Enum.valueOf(BonusType, s);
    }

    public static final BonusType MONTHLY;
    public static final BonusType YEARLY;
    public static final BonusType ONE_OFF;
    public final int id;
    private static final BonusType ENUM$VALUES[];

    static 
    {
        MONTHLY = new BonusType("MONTHLY", 0, 1);
        YEARLY = new BonusType("YEARLY", 1, 2);
        ONE_OFF = new BonusType("ONE_OFF", 2, 3);
        ENUM$VALUES = (new BonusType[] {
            MONTHLY, YEARLY, ONE_OFF
        });
    }
}

看到这一点很明显ordinal()不稳定的原因。它是i中的super(s, i);。我也很悲观,你可以想到一个比你已经列举过的更优雅的解决方案。所有的词汇都是最后一堂课。

答案 12 :(得分:1)

我不确定它在Java中是否相同,但C中的枚举类型也会自动映射到整数,因此您可以使用类型或整数来访问它。您是否尝试过使用整数访问它?

答案 13 :(得分:0)

Int -->String :

public enum Country {

    US("US",0),
    UK("UK",2),
    DE("DE",1);


    private static Map<Integer, String> domainToCountryMapping; 
    private String country;
    private int domain;

    private Country(String country,int domain){
        this.country=country.toUpperCase();
        this.domain=domain;
    }

    public String getCountry(){
        return country;
    }


    public static String getCountry(String domain) {
        if (domainToCountryMapping == null) {
            initMapping();
        }

        if(domainToCountryMapping.get(domain)!=null){
            return domainToCountryMapping.get(domain);
        }else{
            return "US";
        }

    }

     private static void initMapping() {
         domainToCountryMapping = new HashMap<Integer, String>();
            for (Country s : values()) {
                domainToCountryMapping.put(s.domain, s.country);
            }
        }

答案 14 :(得分:0)

我需要不同的东西,因为我想使用通用方法。我正在从字节数组中读取枚举。这是我想出的地方:

public interface EnumConverter {
    public Number convert();
}



public class ByteArrayConverter {
@SuppressWarnings("unchecked")
public static Enum<?> convertToEnum(byte[] values, Class<?> fieldType, NumberSystem numberSystem) throws InvalidDataException {
    if (values == null || values.length == 0) {
        final String message = "The values parameter must contain the value";
        throw new IllegalArgumentException(message);
    }

    if (!dtoFieldType.isEnum()) {
        final String message = "dtoFieldType must be an Enum.";
        throw new IllegalArgumentException(message);
    }

    if (!EnumConverter.class.isAssignableFrom(fieldType)) {
        final String message = "fieldType must implement the EnumConverter interface.";
        throw new IllegalArgumentException(message);
    }

    Enum<?> result = null;
    Integer enumValue = (Integer) convertToType(values, Integer.class, numberSystem); // Our enum's use Integer or Byte for the value field.

    for (Object enumConstant : fieldType.getEnumConstants()) {
        Number ev = ((EnumConverter) enumConstant).convert();

        if (enumValue.equals(ev)) {
            result = (Enum<?>) enumConstant;
            break;
        }
    }

    if (result == null) {
        throw new EnumConstantNotPresentException((Class<? extends Enum>) fieldType, enumValue.toString());
    }

    return result;
}

public static byte[] convertEnumToBytes(Enum<?> value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
    if (!(value instanceof EnumConverter)) {
        final String message = "dtoFieldType must implement the EnumConverter interface.";
        throw new IllegalArgumentException(message);
    }

    Number enumValue = ((EnumConverter) value).convert();
    byte[] result = convertToBytes(enumValue, requiredLength, numberSystem);
    return result;
}

public static Object convertToType(byte[] values, Class<?> type, NumberSystem numberSystem) throws InvalidDataException {
    // some logic to convert the byte array supplied by the values param to an Object.
}

public static byte[] convertToBytes(Object value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
    // some logic to convert the Object supplied by the'value' param to a byte array.
}
}

枚举的例子:

public enum EnumIntegerMock implements EnumConverter {
    VALUE0(0), VALUE1(1), VALUE2(2);

    private final int value;

    private EnumIntegerMock(int value) {
        this.value = value;
    }

public Integer convert() {
    return value;
}

}

public enum EnumByteMock implements EnumConverter {
    VALUE0(0), VALUE1(1), VALUE2(2);

    private final byte value;

    private EnumByteMock(int value) {
        this.value = (byte) value;
    }

    public Byte convert() {
        return value;
    }
}

答案 15 :(得分:0)

给定:

公共枚举BonusType {   每月(0),每年(1),一次关闭(2) }

BonusType奖励=每年;

System.out.println(bonus.Ordinal()+“:” +红利)

输出: 1:每年

答案 16 :(得分:0)

如果您有课程汽车

public class Car {
    private Color externalColor;
}

属性颜色是一个类

@Data
public class Color {
    private Integer id;
    private String name;
}

您想将颜色转换为枚举

public class CarDTO {
    private ColorEnum externalColor;
}

只需在 Color 类中添加一种方法即可转换 ColorEnum

中的 Color
@Data
public class Color {
    private Integer id;
    private String name;

    public ColorEnum getEnum(){
        ColorEnum.getById(id);
    }
}

ColorEnum 内部实现了getById()方法

public enum ColorEnum {
...
    public static ColorEnum getById(int id) {
        for(ColorEnum e : values()) {
            if(e.id==id) 
                return e;
        }
    }
}

现在您可以使用classMap

private MapperFactory factory = new DefaultMapperFactory.Builder().build();
...
factory.classMap(Car.class, CarDTO.class)
    .fieldAToB("externalColor.enum","externalColor")
    .byDefault()
    .register();
...
CarDTO dto = mapper.map(car, CarDTO.class);