我应该尝试用Java创建可逆枚举,还是有更好的方法?
我似乎已经多次面对这个问题,我想问社区我是不是找错了方向。基本上,我的问题可以归结为:如果我有一个枚举(在Java中),值对它很重要,我应该使用枚举还是有更好的方法,如果我使用枚举,那么什么是反转查找的最佳方法 这里有一个例子。假设我想创建一个表示特定月份和年份的bean。我可能会创建如下内容:我应该尝试用Java创建可逆枚举,还是有更好的方法?,java,enums,idioms,Java,Enums,Idioms,我似乎已经多次面对这个问题,我想问社区我是不是找错了方向。基本上,我的问题可以归结为:如果我有一个枚举(在Java中),值对它很重要,我应该使用枚举还是有更好的方法,如果我使用枚举,那么什么是反转查找的最佳方法 这里有一个例子。假设我想创建一个表示特定月份和年份的bean。我可能会创建如下内容: public interface MonthAndYear { Month getMonth(); void setMonth(Month month); int getYear
public interface MonthAndYear {
Month getMonth();
void setMonth(Month month);
int getYear();
void setYear(int year);
}
在这里,我将我的月份存储为一个单独的类,名为month,因此它是类型安全的。如果我只输入int,那么任何人都可以将13、5643或-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);
}
}
公共类ReversibleHashMap扩展了java.util.HashMap{
私有java.util.HashMap reverseMap;
公共可逆hashmap(){
超级();
reverseMap=newjava.util.HashMap();
}
@凌驾
公共V-put(K,V-V){
反向映射put(v,k);
返回super.put(k,v);
}
公共K反转(V){
返回反向映射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));
}
}
公共枚举月{
一月,,
二月,,
前进
四月,,
也许
六月,,
七月,,
八月,,
九月,,
十月,,
十一月,,
十二月,;
私有静态可逆hashmap monthNumMap;
静止的{
monthNumMap=新的ReversibleHashMap();
monthNumMap.put(新java.lang.Integer(1),一月);
monthNumMap.put(新java.lang.Integer(2),2月);
monthNumMap.put(新java.lang.Integer(3),三月);
monthNumMap.put(新java.lang.Integer(4),4月);
monthNumMap.put(新java.lang.Integer(5),五月);
monthNumMap.put(新java.lang.Integer(6),6月);
monthNumMap.put(新java.lang.Integer(7),7月);
monthNumMap.put(新java.lang.Integer(8),8月);
monthNumMap.put(新java.lang.Integer(9),9月);
monthNumMap.put(新java.lang.Integer(10),10月);
monthNumMap.put(新java.lang.Integer(11),11月);
monthNumMap.put(新java.lang.Integer(12),12月);
}
public int getMonthNum(){
返回月份倒转日期(本);
}
公共静态月自整数(整数月){
返回monthNumMap.get(新的java.lang.Integer(monthNum));
}
}
现在这一切我想它,但它仍然看起来是错误的。人们向我建议“如果枚举具有有意义的内部值,则应使用常量”。然而,我不知道这种方法将如何为我提供我正在寻找的类型安全性。但我的发展方式似乎过于复杂了。有没有标准的方法来做这类事情
PS:我知道政府增加一个新月份的可能性……相当不可能,但想想更大的图景——Enum有很多用途。有一种更简单的方法可以做到这一点。每个枚举都有一个方法返回其编号(从零开始)
这是一种非常常见的模式,适用于枚举。。。但它可以更简单地实现。不需要“可逆映射”-在构造函数中使用月数的版本更适合从
月
到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;
}
}
在构造函数中,为numberToMonthMap
添加一个静态变量初始值设定项,但这不起作用-您会立即得到一个NullReferenceException
,因为您试图将该值放入一个尚不存在的映射中:(您不应该使用ordinal()对于这种事情,对于样本来说,它可以工作几个月(因为它不会被扩展),但是java中的枚举的一个好处是,它可以被扩展而不会破坏事物。如果你开始依赖于纵(),如果在中间增加一些值,事情就会中断。
我会按照Jon Skeet的建议去做(他在我写这篇文章的时候写的),但是对于内部数字表示在定义良好的范围内(比如0到20)的情况,我可能不会使用HashMap并引入int的自动装箱,而是使用普通数组(比如Month[12]),但两者都可以(乔恩后来更换了职位,加入了这一建议)
编辑:对于有自然顺序(如已排序的月份)的少数枚举,序数()可能可以安全使用。如果您坚持使用,您可能遇到的问题将出现在有人可能更改枚举顺序的情况下。例如:
当将来有人在不知道您依赖序号的情况下扩展程序时,“enum{MALE,FEMALE}”就变成了“enum{UNKNOWN,FEMALE,MALE}”
给Jon一个+1,因为他写的和我刚才写的一样。我的答案可能远远落后于其他人,但我倾向于实现得简单一点。别忘了“Enum”有一个values()方法
调用
Month.values()
Month.values()[ordinalNumber];
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;
}
}
numberToMonthMap.put(monthNum, this);
public static Month parse(int num)
{
for(Month value : values())
{
if (value.monthNum == num)
{
return value;
}
}
return null; //or throw exception if you're of that mindset
}