Кажется, я сталкивался с этой проблемой много раз, и я хотел спросить сообщество, не лаяю ли я просто не на то дерево. В основном мой вопрос можно свести к следующему: если у меня есть перечисление (в Java), для которого важны значения, должен ли я вообще использовать перечисление или есть ли лучший способ, и если я использую перечисление, то что лучший способ отменить поиск?
Вот пример. Предположим, я хочу создать bean-компонент, представляющий определенный месяц и год. Я мог бы создать что-то вроде следующего:
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);
}
}
Затем я реализовал это в своем перечислении вместо метода конструктора:
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: Я знаю, что вероятность того, что правительство добавит новый месяц ... довольно маловероятна, но подумайте о более широкой картине - существует множество применений для перечислений.