Использование enum
enum Numbers {ZERO, ONE, TWO, THREE}
и через ordinal() получать соответствующие номера. Однако, у меня не получилось объявить что-то типа:
enum Numbers {ZERO, TWO = 2, THREE}
чтобы пропустить ненужные константы.
Возможен ли подобный синтаксис в Java?
Читал, что можно определить в enum свои переменные и методы. Таким образом вроде бы можно создать перечисление с двумя конструкторами, которое бы возвращало то, что надо. Но придется для каждого подобного enum описывать те же переменные и функции - получается ненужное дублирование кода. Наследовать же от enum у меня не получилось.
private int number;
private Number(int number) {
this.number = number;
}
public int getNumber() { return number; }
// ну, это уже по надобности.
// хотя скорее всего, в этой задаче именно вот это и надо
public static getByNumber(int number) {
switch (number) {
case 1:
return ONE;
case 2:
return TWO;
}
ONE(1),
TWO(2)
}
}
ZERO, THREE(3), FOUR, EIGHT(8), NINE;
private final int val;
private static int nextVal = 0;
private int IncNextVal(int setVal) {
if (setVal != 0) {
nextVal = setVal;
}
return nextVal++;
}
Numbers() {
val = IncNextVal(0);
}
Numbers(int n) {
val = n;
IncNextVal(val);
}
int GetVal() {
return val;
}
}
Только в моем случае можно произвольное число констант добавить.
Проблема в том, что мне приходиться в каждом enum все эти функции и переменные повторять. А хотелось бы что-то вроде
ZERO, THREE(3), FOUR, EIGHT(8), NINE
}
То есть создать родительский класс с "правильным" перечислением и от него наследовать. Но что-то не получается.
Добавлено позже: В моем варианте надо бы еще исключение добавить (или как-то сообщить компилятору), если пользователь захочет произвести инициализацию числом, меньшем, чем в предыдущей константе. Но пока я сделал по простому.
Однако, массив именованных объектов тоже полезная штука. Но каждый раз определять её интерфейс как-то лениво, да и неправильно. Наверно, тут надо копать в сторону Enum с большой буквы, чтобы можно было наследовать...
Тут не просто именованые объекты, а еще и поддержка сравнения через == и сериализацыи. А именно для того чтоб сравнивать через == гарантируется существование одного екземпляра для каждого мембера перечисления. Отсюда и спецыфика десериализацыи. Если сериализировать, а потом десериализировать елемент перечисления мы всеровно получим один и тот же екземпляр.
Наследовать перечисления дело не совсем безопасное. И похоже в данном случае невозможное изза конструкцыи Class Enum<E extends Enum<E>> (хотя я полностю не уверен что невозможное)
Одной из причин использования енумов вместо констант есть дополнительный контроль типов. Если будет наследование - появится возможность приведения до базового типа. Тут же и неоднозначность реализацыи equals().
Можно поинтересоватся зачем понадобился енум с явным указанием ordinal ?
В любом случае если нечто подобное нужно, его можно реализировать самостоятельно в обход стандартного енума. Правда без потерь думаю не обойдестя. Наверно от switch придется отказатся.
Бывает нужен, например, для битовых масок в C#:
public enum MyEnum : int {
Option1 = 1,
Option2 = 2,
Option3 = 4
}
Хотя то, что сделал Kogrom, больше напоминает реализацию вариантного типа (из Nemerle).
Можно поинтересоватся зачем понадобился енум с явным указанием ordinal ?
Например, в C++ у меня была программа, которая использовала 2 енума из разных классов, причем не было одинковых констант (грубо говоря с одинаковым ordinal), так как во втором enum порядок начинался с определенного номера. Понятно, что это можно легко обойти.
Пример: есть инкапсулированный в классе первый набор констант, идущих подряд, и открытый второй набор состоящий из двух констант, которые равны первому и последнему элементу из первого набора.
Тогда внешний объект может получить какую-то переменную и если она находится в диапазоне, который задан двумя открытыми константами, то переменная передается тому объекту, у которого есть доступ к инкапсулированному набору констант.
При этом программист может не задумываться о значении констант.
Извините за сложные формулировки.
Далее. Иногда в C++ удобно применить enum как маску, фильтр определенных значений. В этом случае тоже хорошо бы иметь возможность пропускать значения в enum.
enum E {
A(1), B(5), C(18);
int i;
E(int i) { this.i = i; }
}
PS. Помните, что в Java enum - это класс. У него могут быть свои методы, поля и даже контструкторы. Возможно использовать даже полиморфизм.
Для глубокого понимания еще раз рекомендую вышеуказанную статью.
enum E {
A(1), B(5), C(18);
int i;
E(int i) { this.i = i; }
}
PS. Помните, что в Java enum - это класс. У него могут быть свои методы, поля и даже контструкторы. Возможно использовать даже полиморфизм.
Для глубокого понимания еще раз рекомендую вышеуказанную статью.
О оно че =))
Для глубокого понимания, стоит запомнить, что enum'ми не стоит злоупотреблять. зачастую они просто используются для задания опций и для дальнейшего перенаправления enum'a в метод, т.е. используется в сигнатуре метода. Удобно и пракично.
Ну например, вот так вот (используется nio.2 из jdk7):
for (i=0; i<source.length; i++) {
Path dest = (isDir) ? target.resolve(source.getName()) : target;
if (recursive) {
// follow links when copying files
EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
TreeCopier tc = new TreeCopier(source, dest, prompt, preserve);
Files.walkFileTree(source, opts, Integer.MAX_VALUE, tc);
} else {
// not recursive so source must not be a directory
try {
if (Attributes.readBasicFileAttributes(source).isDirectory()) {
System.err.format("%s: is a directory%n", source);
continue;
}
} catch (IOException x) { }
copyFile(source, dest, prompt, preserve);
}
}