我似乎多次遇到这个问题,我想问问社区我是不是找错人了。基本上我的问题可以归结为:如果我有一个值很重要的枚举(在 Java 中),我应该使用枚举还是有更好的方法,如果我确实使用枚举那么什么是反转查找的最佳方法吗?
这是一个例子。假设我想创建一个代表特定月份和年份的 bean。我可能会创建如下内容:
public interface MonthAndYear {
Month getMonth();
void setMonth(Month month);
int getYear();
void setYear(int year);
}
在这里,我将我的月份存储为一个名为 Month 的单独类,因此它是类型安全的。如果我只输入 int,那么任何人都可以将 13 或 5,643 或 -100 作为数字传递,并且无法在编译时检查它。我限制他们输入我将作为枚举实现的月份:
public enum Month {
JANUARY,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER;
}
现在假设我有一些我想写入的后端数据库,它只接受整数形式。那么标准的方法似乎是:
public enum Month {
JANUARY(1),
FEBRUARY(2),
MARCH(3),
APRIL(4),
MAY(5),
JUNE(6),
JULY(7),
AUGUST(8),
SEPTEMBER(9),
OCTOBER(10),
NOVEMBER(11),
DECEMBER(12);
private int monthNum;
public Month(int monthNum) {
this.monthNum = monthNum;
}
public getMonthNum() {
return monthNum;
}
}
相当简单,但如果我想从数据库中读取这些值并写入它们,会发生什么情况?我可以在枚举中使用 case 语句实现一个静态函数,该函数接受一个 int 并返回相应的 Month 对象。但这意味着如果我改变了什么,那么我将不得不改变这个函数以及构造函数参数——改变两个地方。所以这就是我一直在做的。首先,我创建了一个可逆映射类,如下所示:
public class ReversibleHashMap<K,V> extends java.util.HashMap<K,V> {
private java.util.HashMap<V,K> reverseMap;
public ReversibleHashMap() {
super();
reverseMap = new java.util.HashMap<V,K>();
}
@Override
public V put(K k, V v) {
reverseMap.put(v, k);
return super.put(k,v);
}
public K reverseGet(V v) {
return reverseMap.get(v);
}
}
然后我在我的枚举中而不是构造函数方法中实现了它:
public enum Month {
JANUARY,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER;
private static ReversibleHashMap<java.lang.Integer,Month> monthNumMap;
static {
monthNumMap = new ReversibleHashMap<java.lang.Integer,Month>();
monthNumMap.put(new java.lang.Integer(1),JANUARY);
monthNumMap.put(new java.lang.Integer(2),FEBRUARY);
monthNumMap.put(new java.lang.Integer(3),MARCH);
monthNumMap.put(new java.lang.Integer(4),APRIL);
monthNumMap.put(new java.lang.Integer(5),MAY);
monthNumMap.put(new java.lang.Integer(6),JUNE);
monthNumMap.put(new java.lang.Integer(7),JULY);
monthNumMap.put(new java.lang.Integer(8),AUGUST);
monthNumMap.put(new java.lang.Integer(9),SEPTEMBER);
monthNumMap.put(new java.lang.Integer(10),OCTOBER);
monthNumMap.put(new java.lang.Integer(11),NOVEMBER);
monthNumMap.put(new java.lang.Integer(12),DECEMBER);
}
public int getMonthNum() {
return monthNumMap.reverseGet(this);
}
public static Month fromInt(int monthNum) {
return monthNumMap.get(new java.lang.Integer(monthNum));
}
}
现在这完成了我想要的一切,但它看起来仍然不对。人们向我建议“如果枚举具有有意义的内部值,您应该改用常量”。但是,我不知道这种方法如何为我提供我正在寻找的类型安全。不过,我开发的方式似乎过于复杂。有没有一些标准的方法来做这种事情?
PS:我知道政府增加新月份的可能性是......相当不可能,但想想更大的图景 - 枚举有很多用途。
最佳答案
这是一个非常常见的模式,它适用于枚举……但它可以更简单地实现。不需要“可逆映射”——在构造函数中采用月份数字的版本更适合从 Month
到 int
。但走另一条路也不难:
public enum Month {
JANUARY(1),
FEBRUARY(2),
MARCH(3),
APRIL(4),
MAY(5),
JUNE(6),
JULY(7),
AUGUST(8),
SEPTEMBER(9),
OCTOBER(10),
NOVEMBER(11),
DECEMBER(12);
private static final Map<Integer, Month> numberToMonthMap;
private final int monthNum;
static {
numberToMonthMap = new HashMap<Integer, Month>();
for (Month month : EnumSet.allOf(Month.class)) {
numberToMonthMap.put(month.getMonthNum(), month);
}
}
private Month(int monthNum) {
this.monthNum = monthNum;
}
public int getMonthNum() {
return monthNum;
}
public static Month fromMonthNum(int value) {
Month ret = numberToMonthMap.get(value);
if (ret == null) {
throw new IllegalArgumentException(); // Or just return null
}
return ret;
}
}
在您知道从 1 到 N 的数字的特定情况下,您可以简单地使用数组 - 获取 Month.values()[value - 1]
或缓存返回值Month.values()
以防止在每次调用时都创建一个新数组。 (正如 cletus 所说,getMonthNum
可以只返回 ordinal() + 1
。)
但是,在值可能无序或分布稀疏的更一般情况下,值得注意上述模式。
重要的是要注意静态初始化器在所有枚举值创建之后执行。最好只写
numberToMonthMap.put(monthNum, this);
在构造函数中并为 numberToMonthMap
添加一个静态变量初始值设定项,但这不起作用 - 你会立即得到一个 NullReferenceException
,因为你会尝试将值放入尚不存在的 map 中:(
关于java - 我应该尝试在 Java 中创建可逆枚举还是有更好的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1582940/