Try English version of Quizful



Раздаем бесплатные Q! подробности в группе Quizful.Alpha-test
Партнеры
Рекрутерам: Прескрининг кандидатов about
Топ контрибуторов
loading
loading
Знаете ли Вы, что

Лучшие IT работодатели регулярно просматривают рейтинги и профили пользователей в поисках кандидатов. Для корректного отображения ваших данных рекомендуем заполнить ваш профиль и добавить информацию о вас и вашей профессии.

Лента обновлений
ссылка 21:37:54
Комментарий от wohan:
Ку!
ссылка 17:12:04
Комментарий от hudrogen1:
сложна
ссылка 13:59:43
Комментарий от misha_057:
- присвоить значение, отличное от null локальной перем...
ссылка 13:06:39
Добавлен вопрос в тест Servlets
ссылка 11:48:27
Комментарий от Romzik:
Что можно сказать по вопросу без пояснения.
Значить челов...
Статистика

Тестов: 152, вопросов: 8527. Пройдено: 362490 / 1752441.

Перечисления в Java (java enum)

head tail Статья
категория
Java
дата15.05.2009
авторyohan
голосов161

Предистория

Программируя мы часто сталкиваемся с необходимостью ограничить множество допустимых значений для некоторого типа данных. Так, например, день недели может иметь 7 разных значений, месяц в году - 12, а время года - 4. Для решения подобных задач во многих языках программирования со статической типизацией предусмотрен специальный тип данных - перечисление (enum). В Java перечисление появилось не сразу. Специализированная языковая конструкция enum была введена начиная с версии 1.5. До этого момента программисты использовали другие методы для реализации перечислений.

Конструкция enum

Начнем с примера. Давайте опишем с помощью enum тип данных для хранения времени года:

enum Season { WINTER, SPRING, SUMMER, AUTUMN }

Ну и простой пример его использования:

Season season = Season.SPRING;
if (season == Season.SPRING) season = Season.SUMMER;
System.out.println(season);

В результате выполнения которого на консоль будет выведено SUMMER. Думаю, что пример очевиден и в пояснениях не нуждается.

Перечисление - это класс

Объявляя enum мы неявно создаем класс производный от java.lang.Enum. Условно конструкция enum Season { ... } эквивалентна class Season extends java.lang.Enum { ... }. И хотя явным образом наследоваться от java.lang.Enum нам не позволяет компилятор, все же в том, что enum наследуется, легко убедиться с помощью reflection:

System.out.println(Season.class.getSuperclass());

На консоль будет выведено:

class java.lang.Enum

Собственно наследование за нас автоматически выполняет компилятор Java. Далее давайте условимся называть класс, созданный компилятором для реализации перечисления - enum-классом, а возможные значения перечисляемого типа - элементами enum-a.

Элементы перечисления - экземпляры enum-класса, доступные статически

Элементы enum Season (WINTER, SPRING и т.д.) - это статически доступные экземпляры enum-класса Season. Их статическая доступность позволяет нам выполнять сравнение с помощью оператора сравнения ссылок ==. Пример:

Season season = Season.SUMMER;
if (season == Season.AUTUMN) season = Season.WINTER;

Название и порядковый номер элемента enum

Как уже было сказано ранее любой enum-класс наследует java.lang.Enum, который содержит ряд методов полезных для всех перечислений. Пример:

Season season = Season.WINTER;
System.out.println("season.name()=" + season.name() + " season.toString()=" + season.toString() + " season.ordinal()=" + season.ordinal());

Будет выведено:

season.name()=WINTER season.toString()=WINTER season.ordinal()=0

Здесь показано использования методов name(), toString() и ordinal(). Семантика методов - очевидна. Следует обратить внимание, что данные методы enum-класс наследует из класса java.lang.Enum

Получение елемента enum по строковому представлению его имени

Довольно часто возникает задача получить элемент enum по его строковому представлению. Для этих целей в каждом enum-классе компилятор автоматически создает специальный статический метод: public static EnumClass valueOf(String name), который возвращает элемент перечисления EnumClass с названием, равным name. Пример использования:

String name = "WINTER";
Season season = Season.valueOf(name);

В результате выполнения кода переменная season будет равна Season.WINTER. Cледует обратить внимание, что если элемент не будет найден, то будет выброшен IllegalArgumentException, а в случае, если name равен null - NullPointerException. Об этом, кстати, часто забывают. Почему-то многие твердо уверенны, что если функция принимает один аргумент и при некоторых услових выбрасывает IllegalArgumentException, то при передачи туда null, также будет неприменно выброшен IllegalArgumentException. Но это не относится к делу. Продолжим.

Получение всех элементов перечисления

Иногда необходимо получить список всех элементов enum-класса во время выполнения. Для этих целей в каждом enum-классе компилятор создает метод:
public static EnumClass[] values(). Пример использования:

System.out.println(Arrays.toString(Season.values()));

Получим вывод:

[WINTER, SPRING, SUMMER, AUTUMN]

Обратите внимание, что ни метод valueOf(), ни метод values() не определен в классе java.lang.Enum. Вместо этого они автоматически добавляются компилятором на этапе компиляции enum-класса.

Добавляем свои методы в enum-класс

У Вас есть возможность добавлять собственные методы как в enum-класс, так и в его элементы:

enum Direction {
   UP, DOWN;

   public Direction opposite() { return this == UP ? DOWN : UP; }
}

То же, но с полиморфизмом:

enum Direction {
   UP {
        public Direction opposite() { return DOWN; }
   },
   DOWN {
        public Direction opposite() { return UP; }
   };

   public abstract Direction opposite();
}

Последний пример демонстрирует использование наследования в enum. Об этом - далее.

Наследование в enum

С помощью enum в Java можно реализовать иерархию классов, объекты которой создаются в единственном экземпляре и доступны статически. При этом элементы enum могут содержать собственные конструкторы. Приведем пример:

enum Type {
    INT(true) {
        public Object parse(String string) { return Integer.valueOf(string); }
    },
    INTEGER(false) {
        public Object parse(String string) { return Integer.valueOf(string); }
    },
    STRING(false) {
        public Object parse(String string) { return string; }
    };

    boolean primitive;
    Type(boolean primitive) { this.primitive = primitive; }

    public boolean isPrimitive() { return primitive; }
    public abstract Object parse(String string);
}

Здесь объявляется перечисление Type с тремя элементами INT, INTEGER и STRING. Компилятор создаст следующие классы и объекты:

  • Type - класс производный от java.lang.Enum
  • INT - объект 1-го класса производного от Type
  • INTEGER - объект 2-го класса производного от Type
  • STRING - объект 3-го класса производного от Type

Три производных класса будут созданы с полиморфным методом Object parse(String) и конструктором Type(..., boolean) При этом объекты классов INT, INTEGER и STRING существуют в единственном экземпляре и доступны статически. В этом можно убедится:

System.out.println(Type.class);
System.out.println(Type.INT.getClass() + " " + Type.INT.getClass().getSuperclass());
System.out.println(Type.INTEGER.getClass() + " " + Type.INTEGER.getClass().getSuperclass());
System.out.println(Type.STRING.getClass()  + " " + Type.STRING.getClass().getSuperclass());

Получим вывод:

class Type
class Type$1 class Type
class Type$2 class Type
class Type$3 class Type

Видно, что компилятор создал класс Type и 3 nested класса, производных от Type.

Декомпилированный enum-class с наследованием

В подтверждение вышесказанному приведем еще результат декомпиляции перечисления Type из примера выше:

abstract class Type extends Enum {
    public static Type[] values() {
        return (Type[]) $VALUES.clone();
    }

    public static Type valueOf(String name) {
        return (Type) Enum.valueOf(t / T$Type, name);
    }

    public boolean isPrimitive() {
        return primitive;
    }

    public abstract Object parse(String s);

    public static final Type INT;
    public static final Type INTEGER;
    public static final Type STRING;
    boolean primitive;
    private static final Type $VALUES[];

    static {
        INT = new Type("INT", 0, true) {
            public Object parse(String string) { return Integer.valueOf(string); }
        };
        INTEGER = new Type("INTEGER", 1, false) {
            public Object parse(String string) { return Integer.valueOf(string); }
        };
        STRING = new Type("STRING", 2, false) {
            public Object parse(String string) { return string; }
        };

        $VALUES = (new Type[]{
                INT, INTEGER, STRING
        });
    }

    private Type(String s, int i, boolean primitive) {
        super(s, i);
        this.primitive = primitive;
    }
}

Перечисления и параметрический полиморфизм

У читателя может возниктуть вопрос: "почему вышеуказанное перечисление Type не использует генерики (generics) ?". Дело в том, что в Java использование генериков в enum запрещено. Так следующий пример не скомпилируется:

enum Type<T> {}

Дальнейшее изучение

Для более глубокого понимания того, как работают перечисления в Java рекомендую ознакомиться с исходными кодами класса java.lang.Enum, а также воспользоваться декопмилятором Jad для изучения сгенерированного кода. Более того, изучение исходных кодов библиотеки Java абсолютно необходимо для понимания принципов работы многих механизмов в Java и полезно как эталон объектно-ориентированного дизайна.

Также для закрепления знаний рекомендую пройти тесты:
Тест знаний Java - Основы
Тест знаний Java - Средний уровень

--
Дмитрий Пекар, апрель 2009

Если Вам понравилась статья, проголосуйте за нее

Голосов: 161  loading...
admin   c0nst   lexxzar   clumsy   patisonka   breusov   giroy   doctorlife   alexis112   irene_irene   MaxT   fdcc   Dustty   matias   m0211spb   krolser   alniks   fedind   zheka82   javadev75   makk   generator   hatter   DodgeWP   kosolapiy   FAA   uniservise   ruel   bu_ma_ga   Thonatos   Andrew1985   piscar   aprl_mary   abihle   gray2   ayscha   Defend   sheff4ik   oleksa   helpa   JinSem   termy   nsmirnov   wisd   mokeyd   Nastiona   Alexander_1   slyshkov   speedy   Hayao   san   art   JBL   Timfea   Saimon2k   Taky   WDM   dector   alexbnd1   DoubleDragon   safonik   child_ron   Skirkyn   ost   vrnroman   Skrakan   vkozak   Remez   pu6istiy   maxim1987   nikola1   krieger_   dzvinka   VasylZ   KateAlex   amazurok   zheng_bai_qi   ksever   strange_bear   NordLad   fdman   R2D2   michalerain   tu_101   sick_puppie   Sekt104   zy0rg   Demonic   ivanlucky   Nickname_53   xZ_   Quver   alex_skn   piggygoose   inna5   enikeev   Shved90   holy_kachok   koma   St1G   royksopp   smr   GrygorenkoRoman   Chessur   Max64   publo   vladuxa   Gatonegro   SichikUA   GalaranF   Wild_klas   sivakpavel   bardakov   Asdfgh   Metalex   webnurka   VeLKerr   timspinregs   musclexboy   heroin   Riot_Sam   dezmond   tempest   Raido_ddr   ak_brest   atretyakov   DunanNat   Bagzoid   Ksaron   Darwind_   siriusbetta   authentikos   combat1988   Vitaliy21   randriyanov   edivaliev   frederique   LuxCore   polinkot   andru4j   Selfing   Russa   SamTan   jacksolovey   sergss2002   lyapizz   ilko1997   winner   m_borozdenko   tran4rm   jcd3   Axel_m   MashaHalushko   RootS   bahrianyi   ellobo12   pikabu   ArtyJack   vlad15june   Sergey007   barstyler