从枚举序号转换为枚举类型

我有枚举类型ReportTypeEnum,在所有类的方法之间传递,但我需要在URL上传递这个,所以我使用ordinal方法来获取int值。在我在另一个JSP页面中获得它之后,我需要将它转换回ReportTypeEnum,以便我可以继续传递它。

如何将序数转换为ReportTypeEnum?

使用Java 6 SE。

195454 次浏览

要将一个序数转换为它的枚举表示,你可能想这样做:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

请注意数组的边界。

注意,每次调用values()都会返回一个新克隆的数组,这可能会对性能产生负面影响。如果经常调用数组,可能需要缓存数组。

关于如何缓存values()的代码示例


这个回答经过编辑,包含了评论中的反馈

这几乎肯定是一个坏主意。当然,如果序数是实际持久化的(例如,因为有人已收藏了URL)——这意味着你必须在将来始终保留enum顺序,这对代码维护者来说可能不明显。

为什么不使用myEnumValue.name()来编码enum(并通过ReportTypeEnum.valueOf(s)来解码)呢?

可以使用一个静态查找表:

public enum Suit {
spades, hearts, diamonds, clubs;


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


static {
int ordinal = 0;
for (Suit suit : EnumSet.allOf(Suit.class)) {
lookup.put(ordinal, suit);
ordinal+= 1;
}
}


public Suit fromOrdinal(int ordinal) {
return lookup.get(ordinal);
}
}

每个枚举都有name(),它给出一个包含枚举成员名称的字符串。

给定enum Suit{Heart, Spade, Club, Diamond}Suit.Heart.name()将返回Heart

每个枚举都有一个valueOf()方法,该方法接受一个枚举类型和一个字符串,以执行相反的操作:

Enum.valueOf(Suit.class, "Heart")返回Suit.Heart

我真搞不懂为什么会有人用序数。它可能会快几纳秒,但如果枚举成员发生变化,则不安全,因为另一个开发人员可能不知道某些代码依赖于序数值(特别是在问题中引用的JSP页面中,网络和数据库开销完全占据了时间,而不是使用整数而不是字符串)。

如果我要经常使用values():

enum Suit {
Hearts, Diamonds, Spades, Clubs;
public static final Suit values[] = values();
}

同时wherever.java:

Suit suit = Suit.values[ordinal];

如果你想要数组是私有的,请自便:

private static final Suit values[] = values();
public static Suit get(int ordinal) { return values[ordinal]; }


...


Suit suit = Suit.get(ordinal);

注意数组的边界。

我同意大多数人的观点,使用序数可能是一个坏主意。我通常通过给枚举一个私有构造函数来解决这个问题,该构造函数可以以一个DB值为例,然后创建一个静态fromDbValue函数,类似于Jan的回答。

public enum ReportTypeEnum {
R1(1),
R2(2),
R3(3),
R4(4),
R5(5),
R6(6),
R7(7),
R8(8);


private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);
private static Map<Integer, ReportTypeEnum> lookup;
private Integer dbValue;


private ReportTypeEnum(Integer dbValue) {
this.dbValue = dbValue;
}




static {
try {
ReportTypeEnum[] vals = ReportTypeEnum.values();
lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);


for (ReportTypeEnum  rpt: vals)
lookup.put(rpt.getDbValue(), rpt);
}
catch (Exception e) {
// Careful, if any exception is thrown out of a static block, the class
// won't be initialized
log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
}
}


public static ReportTypeEnum fromDbValue(Integer dbValue) {
return lookup.get(dbValue);
}


public Integer getDbValue() {
return this.dbValue;
}


}

现在您可以在不更改查找的情况下更改顺序,反之亦然。

这就是我用的。我并不是说它远不如上面那些简单的解决方案“有效”。当在上面的解决方案中使用无效序数值时,它所做的是提供比"ArrayIndexOutOfBounds"更清晰的异常消息。

它利用了EnumSet javadoc指定迭代器以自然顺序返回元素的事实。如果不正确,就有一个断言。

JUnit4测试演示了如何使用它。

 /**
* convert ordinal to Enum
* @param clzz may not be null
* @param ordinal
* @return e with e.ordinal( ) == ordinal
* @throws IllegalArgumentException if ordinal out of range
*/
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
EnumSet<E> set = EnumSet.allOf(clzz);
if (ordinal < set.size()) {
Iterator<E> iter = set.iterator();
for (int i = 0; i < ordinal; i++) {
iter.next();
}
E rval = iter.next();
assert(rval.ordinal() == ordinal);
return rval;
}
throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}


@Test
public void lookupTest( ) {
java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
System.out.println(tu);
}
public enum Suit implements java.io.Serializable, Comparable<Suit>{
spades, hearts, diamonds, clubs;
private static final Suit [] lookup  = Suit.values();
public Suit fromOrdinal(int ordinal) {
if(ordinal< 1 || ordinal> 3) return null;
return lookup[value-1];
}
}

测试类

public class MainTest {
public static void main(String[] args) {
Suit d3 = Suit.diamonds;
Suit d3Test = Suit.fromOrdinal(2);
if(d3.equals(d3Test)){
System.out.println("Susses");
}else System.out.println("Fails");
}
}

我很感激你与我们分享,如果你有一个更有效的代码,我的enum是巨大的,不断调用数千次。

这是我在Android上使用Proguard所做的:

public enum SomeStatus {
UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order


private static SomeStatus[] values = null;
public static SomeStatus fromInteger(int i) {
if(SomeStatus.values == null) {
SomeStatus.values = SomeStatus.values();
}
if (i < 0) return SomeStatus.values[0];
if (i >= SomeStatus.values.length) return SomeStatus.values[0];
return SomeStatus.values[i];
}
}

它很短,我不需要担心在Proguard有一个异常

所以一种方法是执行__abc0,这很简单,但是, 如前所述,ExampleEnum.values()为每次调用返回一个新的克隆数组。这可能是不必要的昂贵。我们可以通过缓存数组来解决这个问题,就像ExampleEnum[] values = values()。允许修改缓存数组也是“危险的”。有人可以写ExampleEnum.values[0] = ExampleEnum.type2;,所以我会用一个不做额外复制的访问器方法将其设为私有
private enum ExampleEnum{
type0, type1, type2, type3;
private static final ExampleEnum[] values = values();
public static ExampleEnum value(int ord) {
return values[ord];
}
}

你可以使用ExampleEnum.value(ordinal)来获取与ordinal相关的enum值

你可以定义一个简单的方法:

public enum Alphabet{
A,B,C,D;


public static Alphabet get(int index){
return Alphabet.values()[index];
}
}

像这样使用它:

System.out.println(Alphabet.get(2));

安全第一(使用Kotlin):

// Default to null
EnumName.values().getOrNull(ordinal)


// Default to a value
EnumName.values().getOrElse(ordinal) { EnumName.MyValue }

有一种简单和坏的方法,也有一种相当简单和正确的方法。

首先,简单的和坏的(这些通常很受欢迎)。枚举类方法通过值()方法返回所有可用实例的数组,您可以通过数组索引访问枚举对象。

RenderingMode mode = RenderingMode.values()[index];


//Enum Class somewhere else
public enum RenderingMode
{
PLAYING,
PREVIEW,
VIEW_SOLUTION;
    

}
    



//RenderingMode.values()[0] will return RenderingMode.PLAYING
//RenderingMode.values()[1] will return RenderingMode.PREVIEW
//Why this is bad? Because it is linked to order of declaration.


//If you later changed the order here, it will impact all your existing logic around this.
public enum RenderingMode
{
        

PREVIEW,
VIEW_SOLUTION,
PLAYING;
        

}
//Now
//RenderingMode.values()[0] will return RenderingMode.PREVIEW
//RenderingMode.values()[1] will return RenderingMode.VIEW_SOLUTION

这是做这件事的正确方法。 在枚举类中创建一个静态方法fromInt

public enum RenderingMode
{
PLAYING,
PREVIEW,
VIEW_SOLUTION;


public static RenderingModefromInt(int index)
{
//this is independent of order of declaration
switch (index)
{
case 0: return PLAYING;
case 1: return PREVIEW;
case 2: return VIEW_SOLUTION;
}
//Consider throwing Exception here


return null;
}
}
public enum Status {
STATUS_1, STATUS_2, STATUS_3, STATUS_4;


public static Status getStatusByOrdinal(int ordinal) {
for (Status status : values()) {
if (status.ordinal() == ordinal) {
return status;
}
}
return STATUS_1;
}
}

这样你就不能依赖于编译时泛型解析(所以你可以在任何时候创建枚举类实例,即使是那些用Class.forMame创建的类型)

public Object getInstance(Class enumClazz, int ordinal) throws Exception {
Object[] allEnums = enumClazz.getDeclaredMethod("values", Object[].class).invoke(null, null);
return allEnums[ordinal];
}