Enum java что это
Перечисления в Java (java enum)
Предистория
Конструкция enum
Начнем с примера. Давайте опишем с помощью enum тип данных для хранения времени года:
Ну и простой пример его использования:
В результате выполнения которого на консоль будет выведено SUMMER. Думаю, что пример очевиден и в пояснениях не нуждается.
На консоль будет выведено:
Название и порядковый номер элемента enum
Как уже было сказано ранее любой enum-класс наследует java.lang.Enum, который содержит ряд методов полезных для всех перечислений. Пример:
Получение елемента enum по строковому представлению его имени
Довольно часто возникает задача получить элемент enum по его строковому представлению. Для этих целей в каждом enum-классе компилятор автоматически создает специальный статический метод: public static EnumClass valueOf(String name), который возвращает элемент перечисления EnumClass с названием, равным name. Пример использования:
Получение всех элементов перечисления
Иногда необходимо получить список всех элементов enum-класса во время выполнения. Для этих целей в каждом enum-классе компилятор создает метод:
public static EnumClass[] values(). Пример использования:
Обратите внимание, что ни метод valueOf(), ни метод values() не определен в классе java.lang.Enum. Вместо этого они автоматически добавляются компилятором на этапе компиляции enum-класса.
Добавляем свои методы в enum-класс
У Вас есть возможность добавлять собственные методы как в enum-класс, так и в его элементы:
То же, но с полиморфизмом:
Наследование в enum
С помощью enum в Java можно реализовать иерархию классов, объекты которой создаются в единственном экземпляре и доступны статически. При этом элементы enum могут содержать собственные конструкторы. Приведем пример:
Здесь объявляется перечисление Type с тремя элементами INT, INTEGER и STRING. Компилятор создаст следующие классы и объекты:
Три производных класса будут созданы с полиморфным методом Object parse(String) и конструктором Type(. boolean) При этом объекты классов INT, INTEGER и STRING существуют в единственном экземпляре и доступны статически. В этом можно убедится:
Видно, что компилятор создал класс Type и 3 nested класса, производных от Type.
Декомпилированный enum-class с наследованием
В подтверждение вышесказанному приведем еще результат декомпиляции перечисления Type из примера выше:
Перечисления и параметрический полиморфизм
Дальнейшее изучение
Для более глубокого понимания того, как работают перечисления в Java рекомендую ознакомиться с исходными кодами класса java.lang.Enum, а также воспользоваться декопмилятором Jad для изучения сгенерированного кода. Более того, изучение исходных кодов библиотеки Java абсолютно необходимо для понимания принципов работы многих механизмов в Java и полезно как эталон объектно-ориентированного дизайна.
—
Дмитрий Пекар, апрель 2009
Если Вам понравилась статья, проголосуйте за нее
Голосов: 171 Голосовать
Загадки Enum’ов
Перечисления появились в пятой версии Java и с тех пор крепко обосновались в наших приложениях. Работа с перечислениями почти не отличается от работы с любыми другими классами в Java. Но есть несколько особенностей, которые вызывают удивление. Каждый раз сталкиваясь с ними, хочется спросить: «Почему так?».
Давайте попробуем разобраться.
Порядок инициализации
В отличие от некоторых других языков программирования в Java перечисления являются полноценными классами. Конечно, есть некоторые особенности, например:
Хорошо, смирились с запретами. Но можем ли мы ожидать, что остальные языковые конструкции работают так же, как в остальной Java? Например, порядок инициализации объектов.
Давайте проверим. Для этого напишем такое перечисление:
Остановитесь на секунду и попробуйте предположить что выведется в консоль при обращении к любому из значений перечисления.
В обычных классах при инициализации первого объекта кодовые блоки выполняются в следующем порядке:
Для перечисления же мы увидим в консоли следующее:
Как же так? Почему статический блок был вызван последним?
Для ответа на этот вопрос давайте прогоним скомпилированный класс через Java Class File Disassembler и вручную переведем дизассемблированный код в java код. Дизассемблинг выполняется командой:
Для самых любопытных привожу результат исполнения команды.
После ручного перевода в Java код получим следующее (не имеющий отношения к рассматриваемой теме код опущен):
Что же мы видим? Значения перечисления превратились в статические финальные поля. Выражения из кодового блока и конструктора переехали в конструктор. Выражения из статического блока остались в статическом блоке, но до их вызова добавился код создания экземпляров.
Получается, что при первом обращении к классу перечисления первым делом исполняется статический блок. Все как в обычных классах. Первое, что делается в статическом блоке — инициализируются финальные поля класса. Это влечет за собой вызов кода, объявленного в конструкторе, и в консоль дважды выводится
И только после этого исполняется код из статического блока оригинального класса.
Теперь мы знаем в какой класс преобразуется enum при компиляции, и порядок исполнения кодовых блоков становится понятен.
Для того чтобы не держать в памяти последовательность преобразований, приводящую к странному поведению, предлагаю запомнить следующее:
Значения, объявленные в перечислении — это статические финальные поля того же типа, что и класс. Инициализация этих полей происходит в статическом блоке до всех остальных статических выражений.
Отсутствующие методы
Если попробовать в IDE написать любое перечисление, поставить точку и вызвать автодополнение, то он предложит еще два метода:
В исходниках класса Enum таких методов нет, но они как-то появляются в каждом перечислении.
Чтобы разобраться, обратимся к документации. Из нее мы узнаем, что два этих метода объявлены неявно. Почему неявно? Дело в том, что в отличие от других методов класса Enum эти методы не получается реализовать в абстрактном классе. Метод values() возвращает массив со всеми значениями перечисления, а класс Enum о них ничего не знает. Метод valueOf(String) возвращает конкретное значение перечисления по его названию. Можно было бы в нем вызвать метод valueOf(Class, String) :
Но ничего не выходит из-за того, что класс E невозможно извлечь в статическом контексте.
Теперь мы понимаем, что данные методы генерируются компилятором. Но какая же у них реализация? В JLS она не приведена, и в исходниках JDK ее тоже не найти.
Метод valueOf(String) реализуется с помощью вызова тезки:
Обобщая знания о неявных методах и порядке инициализации, давайте запишем как может быть представлено перечисление Pine из начала статьи в виде обычного класса:
Заключение
Странности в перечислениях вызваны архитектурными решениями и ограничениями, выбранными разработчиками Java. С помощью дизассемблирования нам удалось узнать, как перечисления инициализируются, и как в них реализованы неявные методы.
Надеюсь, что теперь, столкнувшись с необычным поведением перечислений, вы сможете мысленно преобразовать перечисление в обычный класс и разобраться.
Привет! Теме что такое Enums (перечисления) мы посвятим 3 статьи:
Ниже наша первая статья. Приятного прочтения.
Что такое Enums
«Зима, весна, лето, осень?»
В жизни часто у нас есть выбор из ограниченного количества вариантов. Это можно сравнить с «меню», из которого мы можем выбирать.
Как представить такое «меню» в коде? Как говорится, лучше один раз увидеть:
Вот мы и создали наш первый Enum! Выглядит просто, правда?
Давайте обратим внимание на ключевые моменты:
Если же enum будет «внутри» класса, он может быть объявлен private:
В конце мы написали точку с запятой.
Пример 1
Мы уже описывали один enum выше. Давайте создадим такой же, только для наглядности назовем его не myEnum, а Seasons(«времена года»):
Enum, все особенности
— Я расскажу тебе об очень интересной штуке. Она довольно простая, но от этого становится еще интереснее. Эта штука называется enum. enum – это тип, заданный перечислением набора значений, которые могут принимать его переменные. Сразу пример:
— Т.е. мы просто перечисляем набор значений и все?
— Да, объявляем enum, внутри через запятую пишем его возможные значения.
Использовать его тоже довольно просто.
— А ему можно присваивать null?
— Да, enum – это обычный класс – вернее, одна из его разновидностей, таких как interface.
— Т.е. везде, где я могу использовать класс, я могу использовать enum?
— А внутри класса можно объявить enum?
— А унаследоваться от enum?
— Нет, ни наследоваться от enum, ни наследовать enum нельзя.
— Потому что Java-компилятор превращает enum примерно вот в это:
Как видно из этого примера:
1) Класс Direction обязательно наследуется от класса Enum, поэтому его нельзя унаследовать больше ни от чего.
2) Класс Direction объявлен final, поэтому от него нельзя ничего унаследовать.
3) Набор значений класса Direction на самом деле – это public static final переменные типа Direction. Что видно по коду их использования:
4) Класс Direction содержит только один конструктор и тот – приватный. Это значит, что объекты класса Direction можно создать только в коде внутри класса. Кроме объявленных объектов, других объектов создать нельзя.
5) Переменным типа Direction можно присвоить ссылку на любой из существующих объектов типа Direction – все они определены у него внутри. Других объектов данного типа нет, и не будет.
6) Объекты типа Direction можно сравнивать с помощью знака «==» — это будет просто сравнение ссылок.
— Не сказал бы, что все очень понятно, но после твоего примера, стало гораздо понятнее.
— Отлично. Тогда вот тебе еще чуток информации:
1) У каждого объекта типа Direction есть его уникальный номер. У первого (UP) – 0, у второго (DOWN) – 1, у третьего (LEFT) – 2, и т.д. Получить этот номер можно с помощью метода ordinal(). Внимание на экран:
2) У каждого enum’а есть метод values(), который возвращает массив значений enum’а.
Т.е. мы можем у любого элемента enum’а получить его номер, а затем по номеру опять получить элемент.
Мы также можем использовать enum в цикле foreach:
— Значит, у enum’а переопределен метод toString? Он же не выводит на экран что-то вроде
«com.javarush.Direction@123fd4»?
— Да, более того у каждого enum’а, а значит и у Direction есть возможность преобразовать его в строку и обратно.
А что будет, если в функцию valueOf передать строку, которой нет в Direction? Например, «AMIGO»?
— А ты сам как думаешь?
На этом наше введение в мир enum’ов окончено.
Enum-Всемогущий
Вводная
Предостережение
В этой статье вы не найдете для себя новых технических знаний, откровений, а если Вы жаждете их — то смело переходите к следующей статье. Тем не менее, здесь есть чему удивляться и есть над чем подумать. Технический юмор и филосовские мысли между строк.
Не рассказанная история.
Однажды попадает разработчик в место, где решают судьбу. Время спустя, перед ним появляется образ и спрашивает:
— Кто ты?
— Я, разработчик, звать Иван, — а про себя: Во встрял то.
Голос опять:
— Хочешь туда?. Взгляд на дверь, за которой рай.
— Ага, — робко Иван.
— Чего поведаешь мне?, — спрашивает Голос.
Немного подумав, Иван начинает говорить:
— Есть в java Enum-Всемогущий.
— Как так, Всемогущий? — перебивает Голос с возмущением. — Это только перечисление!
— Ага, — отвечает разраб, Но не только.
— Докажи!
— Enum как гвозди, утильным могёт.
— Во так чудеса, но… Наследника у него нет!
— А это как посмотреть. А кого считать Наследником? Scala? Kotlin?
— Давай пример, не дожидаясь пока разраб завершит свой вопрос
— Да уж, интересные Вы ребята, прогеры — уже улыбаясь, говорит Голос, — Но малова-то будет
Почесав репу, Иван продолжил:
— Enum-то у нас фабрика!
— Не, было уже.
Пришлось, Ивану последний козырь достать:
— Enum-Синглтон, точно!
Выбери свое |
— Джошуа Блох говорит*, что это лучшая реализация Синглтона.
— Ну а ты?
— А что я? Это, это — это синглтон-фабрика, для хранения одного единственного элемента, тить колотить.
Это точка для доступа к массиву для хранения одного единственного элемента, тить колотить.
Немного выводов
Итого получается, что enum можно наделить следующими качествами и свойствами, в зависимости от точки обзора:
Эксперимент
Я решил понять, сколько можно максимально сгенерировать элементов перечислений. Мой собственный ответ и реальность настолько разошлись, что я усомнился в своих знаниях. Прежде чем Вы посмотрите ниже, попытайтесь дать ответ самостоятельно. Упростим, скажите хотя бы порядок? Вот код, который я использовал для генерации класса перечислений (на быструю руку):
Вы уже предположили? Так вот, на семерочке мне удалось сгенерировать всего 2746 элементов перечислений. А дальше вот это:
Но, так как я раскатал губу в 4 этажа, сначала я получил такую ошибку: