使用与枚举特定的整数设置枚举?

18

大家好。我的问题是我有一组枚举类型并且数据库中存储了与这些枚举类型对应的整数值。例如:

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }
}

我还有一个数据库,它只用整数来表示这些日期,这些整数对应于上面枚举集合中的整数。我想要做的是查询一个数据库,它将返回一个整数,然后根据从数据库返回的整数设置一个对象的枚举器。我可以这样做:

public static Day getDay(int i) {
    switch(i) {
    case 1:
        return Day.SUNDAY;
    case 2: 
        return Day.MONDAY;
    //And so on
    }
}

但是对于一个有超过100个枚举值的枚举集合,这种方法似乎不是很实用。

那么是否有一种方法可以做到这一点呢?我的目标仅仅是插入一个int值并获取一个枚举器,而不必为此集合中的许多枚举类型创建新的方法。也许我应该将其作为自己的类而不是枚举器,但我希望以这种方式来实现。谢谢!


请查看此处的答案和评论:http://stackoverflow.com/questions/3943054/how-can-i-convert-from-a-value-to-an-enum - Soronthar
6个回答

13

这里提供一个简单的实现,可以正常工作:

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }

    public static Day forInt(int id) {
        for (Day day : values()) {
            if (day.fId == id) {
                return day;
            }
        }
        throw new IllegalArgumentException("Invalid Day id: " + id);
    }
}

如果你知道你的枚举从1开始计数,你可以简单地使用这个方法:

public static Day forInt(int id) {
    return values()[id - 1];
}

9
枚举类型有内置的ID号码,即“序数”,您可以利用它来实现这个目的,只要您有一种简单的方法从表示数据库中给定值的数字到枚举类型的序数。序数按照它们在源文件中声明的顺序分配给枚举值,从0开始,因此在您的示例中,SUNDAY的序数为0,MONDAY的序数为1,以此类推。
为了使用它,您只需要将存储在数据库中的整数转换为相应的序数,然后使用它来访问枚举值。因此,对于您的示例,一个实现可能是:
private static Day[] values = Day.values();
public static Day getDay(int i) {
    return values[i - 1];
}

如果枚举值根据它们在数据库中的ID的顺序与它们在源代码中声明的顺序相同,那么您几乎可以直接使用上述方法(除了更改类名)。否则,您可能需要做一些更复杂的工作来映射数据库值到序数;如果映射高度非线性,则可以设置一个数组。

附:当然,如果这样做,就没有fId字段的用处了。


values() 是一个方法而不是字段。代码需要是:return values()[i - 1];(而不是 return value[i - 1];)。请注意在 "values" 后面的括号。 - Bohemian
1
这种方法的问题在于,如果在中间添加一个新的枚举值,很容易意外破坏排序。如果你只在运行时使用值,那么这并不重要,但在这种情况下,OP说它们必须与外部数据库保持同步。 - Matt Solnit
1
@Bohemian:不,EnumClass.values()是一个方法。values是我在代码示例的第一行中定义的字段。(请注意,此代码示例 不是 枚举类本身的一部分) - David Z
@Matt:是的,这就是为什么我添加了最后一段关于将数据库整数映射到序数的内容。当然,我完全同意对于高度非线性的映射(或者经常变化的映射),另一种方法可能更容易些。 - David Z

6
你的枚举类应该内置一个查找映射表作为静态变量。因此,你的枚举类应该像这样:
public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }


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

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

   public static Day getValue(int intValue) {
      return lookup.get(intValue);
   }

}

然后要根据一个整数 i 获取正确的枚举值,只需调用以下方法:

Day.getValue(i);

这种方法即使编号是非线性的或者存在间隔也可以使用。

1

编辑 - 缓存懒加载创建完成。

您可以在运行时创建Map<Integer,Day>并在getDay实现中使用它。

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    private static Map<Integer,Day> CACHE = null;

    private static void lazyFillCache() {
        if (CACHE != null) return;

        final Map<Integer,Day> c = new HashMap<Integer,Day>();
        for (final Day d : Day.values()) {
            c.put(d.fId, d);
        }

        CACHE = c;
    }

    public static Day getDay(int i) {
        lazyFillCache();
        return CACHE.get(i);
    }

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }
}

编辑 - 受Tom Hawtins评论这里的启发,您还可以使用此实现,利用Java类加载功能:

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    private static class Cache {
        private static Map<Integer,Day> CACHE = new HashMap<Integer,Day>();

        static {
            for (final Day d : Day.values()) {
                CACHE.put(d.fId, d);
            }
        }
    }

    public static Day getDay(int i) {
        return Cache.CACHE.get(i);
    }

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }
}

那行不通!当类的静态初始化器运行时,枚举不可用 - 请尝试自己执行。Day.values()在该代码运行时为空。 - Bohemian
是的,我猜可能会发生这种情况,奇怪的是在我的机器上可以工作。也许是一些奇怪的编译器优化。然后我建议在getDay方法中延迟初始化CACHE。 - user124
看起来我的先前实现实际上是可能的,缓存填充发生的静态块必须位于其他所有内容的末尾: https://dev59.com/9XRB5IYBdhLWcg3wuZU1 - user124
我更喜欢通过在私有构造函数中向缓存添加内容来设置缓存。private Day(int id) { this.fId = id; CACHE.put(id,this); } - Andrew Lazarus

0

Day.values() 会返回一个枚举值的(从零开始的)数组。


0

Day myDay = Day.values()[x] - x 是一个整数值


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接