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

sle*_*ske 107 java enums coding-style

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

public enum BonusType {
  MONTHLY, YEARLY, ONE_OFF
}
Run Code Online (Sandbox Code Playgroud)

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

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

从枚举到int很容易:

public enum BonusType {
  public final int id;

  BonusType(int id) {
    this.id = id;
  }
  MONTHLY(1), YEARLY(2), ONE_OFF(3);
}
Run Code Online (Sandbox Code Playgroud)

然后我可以访问int值BonusType x = MONTHLY; int id = x.id;.

但是,我没有看到反向的好方法,即从int到enum.理想情况下,像

BonusType bt = BonusType.getById(2); 
Run Code Online (Sandbox Code Playgroud)

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

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

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

还有其他想法/见解吗?

aio*_*obe 324

enum→int

yourEnum.ordinal()
Run Code Online (Sandbox Code Playgroud)

int→枚举

EnumType.values()[someInt]
Run Code Online (Sandbox Code Playgroud)

String→枚举

EnumType.valueOf(yourString)
Run Code Online (Sandbox Code Playgroud)

枚举→字符串

yourEnum.name()
Run Code Online (Sandbox Code Playgroud)

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

  • 使用`ordinal()`会让我觉得它是一个有问题的解决方案,因为它会在重新排列枚举值列表或删除值时中断.此外,这仅在int值为0 ... n时才是实用的(我经常发现不是这种情况). (17认同)
  • @sleske,如果你开始删除常量,那么无论如何你都会遇到现有的持久数据问题.(在这方面更新了我的答案.) (4认同)
  • 使用`values()`数组只有在所有值为其id的索引编号并按顺序声明时才有效.(我测试了这个以验证如果你声明`FOO(0),BAR(2),BAZ(1);``值[1] == BAR`和`值[2] == BAZ`尽管有id传入.) (3认同)
  • +1这是明显正确的答案.注意,valueOf只有一个参数方法,它只接受一个String并且只要你使用具体的枚举类型就存在(例如`BonusType.valueOf("MONTHLY")`) (2认同)
  • @glowcoder,当然,整数参数只是枚举对象中的一个字段.它与与enum对象关联的序数常量无关(它也可能是一个'double`). (2认同)

Jef*_*eff 37

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我真的很喜欢这个解决方案 看来这是你能得到的最普遍的. (2认同)
  • 真的,真实地阅读了这个问题.如果您正在处理遗留数据库或外部系统,该系统已定义了您不想通过自己的代码传播的整数,那么这正是其中一种情况.序数是一种非常脆弱的方式来保持枚举的价值,除此之外,它在问题中提到的特定情况下是无用的. (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); 
 }
Run Code Online (Sandbox Code Playgroud)

}


Joh*_*yer 7

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

  1. 如果在数据库之外的JVM外部持久存在,则序号不稳定,请不要使用序数.
  2. 使用键值创建静态地图相对容易.

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);
  }
}
Run Code Online (Sandbox Code Playgroud)


Ala*_*ack 5

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

为了节省编写每个Enum的样板代码或重复代码的繁琐工作,我改用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

int-> ValuedEnum:

NRPEPacketType packetType = 
 (NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);
Run Code Online (Sandbox Code Playgroud)