January
По просьбам читателей начну цикл статей, посвященный нововведениям в Java начиная с версии 1.5. Думаю, статьи этой тематики будут актуальны для многих, так как сам только недавно начал переходить на 1.6 (на работе в проекте до сих пор используется версия jdk 1.4).
В этой статье я хочу рассказать о перечислениях (enumeration, enum). Сначала я покажу, как реализовывались перечисления в ранних версиях java.
Итак, перечисление (enum) - это тип, значения которого ограничены конечным набором констант.
Самым простым способом реализовать некоторый набор значений есть следующий класс:
public final class Modifiers {
public static final int PUBLIC = 1;
public static final int STATIC = 2;
public static final int FINAL = 3;
}
Вместе с тем, это и самый ненадежный способ объявления перечисления. Вот некоторые из проблем этого подхода:
- небезопасность (type unsafe): в методе, где требуется передать значение перечисления, можно передать любое число, а не только 1, 2 или 3;
- неинформативность: например, при отладке вывод элемента перечисления не скажет нам ни о чем;
- подверженость ошибкам: например, при добавлении нового элемента или при изменении последовательности существующих.
Хуже этого подхода может быть только использование строк вместо integer. Тогда к вышеперечисленным проблемам добавляется еще и возможная потеря производительности при сравнении двух элементов.
Реализация type safe enum может быть, например, такой (желательно, чтобы Enum еще был Serializable и Comparable):
public abstract class Enum {
protected int id;
protected String name;
protected Enum(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return this.id;
}
public String getName() {
return name;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof Enum)) return false;
final Enum enumer = (Enum) o;
return (id == enumer.id);
}
public int hashCode() {
return id;
}
public String toString() {
return getClass().getName() + "[id=" + id
+ ", name=" + name + "]";
}
protected static Enum getByName(Enum[] enums, String name) {
if (name != null) {
for (int i = 0; i < enums.length; i++) {
if (enums[i].name.equalsIgnoreCase(name)) {
return enums[i];
}
}
}
return null;
}
protected static Enum getById(Enum[] enums, int id) {
for (int i = 0; i < enums.length; i++) {
if (enums[i].id == id) {
return enums[i];
}
}
return null;
}
}
Это абстрактный класс, наследуясь от которого можно создавать различные перечисления.
Вот правильный пример перечилсения, приведенного в начале статьи:
public class Modifiers extends Enum {
public static final int PUBLIC_ID = 1;
public static final String PUBLIC_VIEW = "public";
public static final Modifiers PUBLIC =
new Modifiers(PUBLIC_ID, PUBLIC_VIEW);
public static final int STATIC_ID = 2;
public static final String STATIC_VIEW = "static";
public static final Modifiers STATIC =
new Modifiers(STATIC_ID, STATIC_VIEW);
public static final int FINAL_ID = 3;
public static final String FINAL_VIEW = "final";
public static final Modifiers FINAL =
new Modifiers(FINAL_ID, FINAL_VIEW);
public static final Modifiers[] modifiers = new Modifiers[]{
PUBLIC, STATIC, FINAL
};
private Modifiers(int id, String name) {
super(id, name);
}
public static Modifiers getByView(String code) {
return (Modifiers) getByName(modifiers, code);
}
public static Modifiers getById(int id) {
return (Modifiers) getById(modifiers, id);
}
}
Для хранения в базе данных и сравнения можно использовать ID, а для отладочной информации, логирования или user-friendly отображения используется VIEW.
С выходом jdk версии 1.5 жить стало намного проще. Кроме того, что была предоставлена реализация абстрактного класса Enum (java.lang.Enum), так же для простоты ввели лингвистическую конструкцию enum.
Вернемся к нашему примеру:
public enum Modifiers {
PUBLIC, STATIC, FINAL
}
Проще некуда, правда?
Запись "public enum Modifiers" равноценна "public final class Modifiers extends java.lang.Enum". Это значит, что дальше расширять наш класс мы уже не можем, но можем добавить в него необходимую нам функциональность. А именно:
добавить поля, конструкторы, методы, статические методы. Пример (не пытайтесь найти в нем смысл):
public enum Modifiers {
PUBLIC(1), STATIC(2), FINAL(3);
Modifier(int id) {
this.id = id;
}
private int id;
public int getId() {
return id;
}
public static String getClassName() {
return Modifiers.class.getName();
}
}
Заметьте, что конструктор может быть либо приватным, либо package-private. Для получения дополнительной информации по данной теме обратитесь к реализации абстрактного класса java.lang.Enum. Стоит обратить внимание, что многие методы, переопределяющие методы класса Object, объявлены как final. Так же, для enum не разрешено клонирование.
Если у вас есть какие-либо вопросы, касающиеся использования перечислений, буду рад вам помочь.
12 Comments »
RSS feed for comments on this post.
Я в Java из С++ пришел. И первое, что смутило это отсутствие перечислений. Но потом немножко покопав нововведения я таки нашел этот механизм. Очень удобно. Особенно тем кто много практиковал С/С++.
А самое необычное для приходящих с c|c++ то, что в java у enum-ов могут быть методы. На уровне компилятора в c++ enum-ы преобразовывались в целые числа фактически можно было делать вещи вроде (надеюсь не путаю)
FOO === BAR + 10
В java когда объвляется тип enum, то это аналогично наследованию от класса.
enum HUMANSTATE {
GOOD, BAD, DRUNK
}
public class HackAppProc {
public static void main(String[] args) {
HUMANSTATE s = HUMANSTATE.GOOD;
System.out.println (s.ordinal());
System.out.println (s.name());
System.out.println (s.compareTo(HUMANSTATE.BAD));
for (HUMANSTATE state : HUMANSTATE.values()) {
System.out.println(state);
}
}
}
Интересно было бы почитать как enum-ы реализованы на уровне виртуальной машины в java и есть ли там хитрые хаки оптимизации.
А еще было бы неплохо увидеть статейку про SET-ы они с появлением varargs-синтаксиса и enum-ов стали как конфетки.
Ну а вообще официальный док: http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html и еще очень приятная статья на ibm devworks-ах: http://www.ibm.com/developerworks/ru/library/j-jtp02277/ про то как мученикам от java (сидящих под ранними версиями jdk) можно улучшить свою жизнь.
Тоже недавно на работе на 1.5 перешли.
Статейка, весьма актуальная на данный момент. Обязательно пригодиться. Спасибо.
А можешь что-нибудь езё по Джаве написать ? Интересная это все таки тема.
Спасибо вам за ту инфу, что вы размещаете в блоге!
Меня чем вообще Java смущает, так это тем, что объявление классов и реализация методов находятся вместе. Неудобно как-то смотреть. Вот привык в C++, что в *.H можно быстро посмотреть описание, а в *.cpp реализацию. Просто если класс сложный с кучей кода, то трудно это все окинуть взглядом.
П про Яву скрипт нету блогов Ваших? Заметок? интересных статей? Очень интересуюсь и хочется развиваться ( я недавно занялся им).
Как-нибудь обязательно напишу несколько статей о javascript. Это действительно очень мощный и гибкий язык.
Недавно узнал что в java enum-ах можно делать так:
enum Sex {
MALE(1) {
public void foo() {
System.out.println("MALE::foo");
}
},
FEMALE(0) {
public void foo() {
System.out.println("FEMALE::foo");
}
};
private int sex;
private Sex(int sex) {
this.sex = sex;
}
public int getSize() {
return this.sex;
}
public void foo() {
System.out.println("?");
}
}
и пример использования:
public class TestMalesFemales {
public static Sex getSex() {
return Math.random() МЕНЕЕ_ЧЕМ 0.5 ? Sex.MALE : Sex.FEMALE;
}
public static void main(String[] args) {
getSex().foo();
}
}
Интересненько как в одном классе существуют три версии одной функции да еще с динамическим выбором нужного метода?
Не является ли каждый элемент enum-а фактически отдельным подклассом самого enum-а.
Почти все правильно говоришь.
Запись “public enum Modifiers” равноценна “public final class Modifiers extends java.lang.Enum”. Это специальная лингвистическая конструкция для упрощения синтаксиса. Посмотри класс Enum из пакета java.lang.
>> public final class Modifiers extends java.lang.Enum
это как раз без вопросов, мое представление в том, что класс генерится для каждого из элементов енум-а,
да …
вот только сейчас додумался глянуть результат компиляции (итак, класс Test0, с enum-ом Sex, с двумя элементами):
Test0$Sex.class
Test0$Sex$1.class
Test0$Sex$2.class
или вот еще экспериментик:
System.out.println(Sex.FEMALE.getClass().getSuperclass());
System.out.println(Sex.MALE.getClass().getSuperclass());
на выходе получил, что у обоих элементов enum-а один папа - сам enum:
class testi.Test0$Sex
class testi.Test0$Sex
Интересная инфа, заюзаем!