Что такое enum java
Загадки 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. С помощью дизассемблирования нам удалось узнать, как перечисления инициализируются, и как в них реализованы неявные методы.
Надеюсь, что теперь, столкнувшись с необычным поведением перечислений, вы сможете мысленно преобразовать перечисление в обычный класс и разобраться.
Java Enum перечисления с примерами
Enums – это специальный тип, используемый для определения коллекций констант. Точнее, тип перечисления – это особый вид класса Java, который может содержать константы, методы и т. д. Были добавлены в Java 5. Это руководство объясняет, как создавать и использовать их.
Пример Enum
Вот простой пример:
Обратите внимание на ключевое слово enum, которое используется вместо класса или интерфейса. Оно сообщает компилятору, что это определение типа является перечислением.
Вы можете сослаться на константы в перечислении выше, как это:
Обратите внимание, что переменная уровня имеет тип Level, который является типом перечисления, определенным в примере выше. Переменная уровня может принимать одну из констант уровня Level в качестве значения (HIGH, MEDIUM или LOW). В этом случае уровень установлен на HIGH.
В заявлениях if
Поскольку перечисления являются константами, вам часто приходится сравнивать переменную, указывающую на константу перечисления, с возможными константами в типе перечисления:
Этот код сравнивает переменную уровня с каждой из возможных констант перечисления в перечислении уровня.
Если одно из значений встречается чаще, чем другие, проверка этого значения в первом операторе if приведет к лучшей производительности, так как выполняется меньше сравнений в среднем. Это не большая разница, если только сравнения не выполняются.
В выражениях
Если ваши типы перечислений содержат много констант, и вам нужно проверить переменную по значениям, как показано в предыдущем разделе, использование оператора переключения switch может быть хорошей идеей:
Замените … на код, который нужно выполнить, если переменная уровня соответствует заданному значению константы уровня. Код может быть простой операцией, вызовом метода и т. д.
Итерации
Вы можете получить массив всех возможных значений типа enum, вызвав его метод static values(). Все типы перечислений получают статический метод values() автоматически компилятором. Вот пример итерации всех значений:
Выполнение этого кода распечатало бы все значения:
Обратите внимание, как распечатываются имена самих констант. Это одна из областей, где перечисления отличаются от статических конечных констант.
toString()
Класс enum автоматически получает метод toString() в классе при компиляции. Он возвращает строковое значение имени данного экземпляра перечисления:
Значением переменной levelText после выполнения вышеприведенного оператора будет текст HIGH.
Printing
Если вы печатаете перечисление, вот так:
Затем метод toString() будет вызван и значение, которое будет напечатано, является текстовым именем экземпляра enum. Другими словами, в приведенном выше примере текст HIGH был бы напечатан.
valueOf()
Класс enum автоматически получает статический метод valueOf() в классе при компиляции. Его можно использовать для получения экземпляра класса для данного значения String:
Переменная уровня будет указывать на Level.HIGH после выполнения этой строки.
Fields
Вы можете добавить поля в перечисление. Таким образом, каждое значение константы enum получает их. Значения полей должны быть предоставлены конструктору перечисления при определении констант:
Обратите внимание, что перечисление в приведенном выше примере имеет конструктор, который принимает int. Он устанавливает поле int. Когда постоянные значения перечисления определены, значение int передается в конструктор Java.
Конструктор enum должен быть либо закрытым, либо областью действия пакета (по умолчанию). Вы не можете использовать публичные или защищенные.
Методы
Вы также можете добавлять методы:
Вы вызываете метод через ссылку на одно из значений констант:
Этот код выведет значение 3, которое является значением поля levelCode для константы перечисления HIGH.
Вы не ограничены простыми методами получения и установки. Можете создавать методы, которые производят вычисления на основе значений полей константы перечисления. Если поля не объявлены как final, вправе изменить значения полей (хотя это может быть не очень хорошей идеей, учитывая, что перечисления должны быть константами).
Abstract методы
Для класса enum также могут быть абстрактные методы. Если у класса есть абстрактный метод, то каждый экземпляр класса должен его реализовывать:
Обратите внимание на объявление абстрактного метода внизу класса enum. Каждый экземпляр enum (каждая константа) определяет собственную реализацию этого метода. Его использование полезно, когда нужна отдельная реализация метода для каждого экземпляра перечисления.
Реализация интерфейса
Enum может реализовать интерфейс, если чувствуете, что это имеет смысл в вашей ситуации:
Это метод getDescription(), который приходит из интерфейса MyInterface.
Реализация интерфейса с Enum может использоваться для реализации набора различных констант Comparator, которые применяются для сортировки коллекций объектов.
EnumSet
Специальная реализация Java Set под названием EnumSet, которая может хранить перечисления более эффективно, чем стандартные реализации:
После создания вы можете использовать EnumSet, как и любой другой набор.
EnumMap
Специальная реализация Java Map, которая может использовать экземпляры перечисления в качестве ключей:
Что такое перечисления в Java? (java enum)
Перечисление или java enum – это набор именованных констант, который помогает в определении своих собственных типов данных. Когда вы можете определить тип переменных в программе, становится легко их определить.
Перечисление – это в основном список именованных констант. В Java это определяет тип класса. Он может иметь конструкторы, методы и переменные экземпляра. Он создается с помощью ключевого слова enum. По умолчанию каждая константа перечисления имеет вид.
Хотя перечисление определяет тип класса и имеет конструкторы, вам не нужно создавать экземпляр перечисления с помощью новой переменной. Переменные перечисления используются и объявляются так же, как переменные примитива.
Объявление enum может быть сделано вне Класса или внутри Класса. Но мы не можем объявить Enum внутри метода. Давайте рассмотрим небольшой пример, чтобы понять его декларацию.
1. Объявление перечисления в Java вне класса
2. Объявление перечисления в Java внутри класса
Первая строка внутри типа enum должна быть списком констант. Затем вы можете использовать методы, переменные и конструктор. По сути, enum представляет группу переменных и констант.
Перечисление с использованием оператора Switch
Значение перечисления также может использоваться для управления оператором switch. Необходимо, чтобы все операторы case использовали константы из того же перечисления, что и оператор switch. Ниже пример демонстрирует то же самое.
Values() и метод ValueOf()
Values(): при создании enum компилятор Java внутренне добавляет метод values(). Этот метод возвращает массив, содержащий все значения перечисления.
ValueOf(): этот метод используется для возврата константы перечисления, значение которой равно строке, переданной в качестве аргумента при вызове этого метода.
Теперь давайте напишем программу, чтобы понять эти методы более подробно.
Вот как вы можете использовать метод Values(), чтобы вернуть массив, содержащий все перечисления, присутствующие в методе, и Valueof(), чтобы вернуть константу перечисления.
Перечисление с помощью конструктора, переменной экземпляра и метода
По сути, перечисление может содержать конструктор и выполняется отдельно для каждой константы перечисления во время загрузки класса перечисления. Кроме того, перечисление может создавать конкретные методы.
Здесь, как только мы объявляем переменную enum(), конструктор вызывается один раз, и он инициализирует параметр age для каждой константы перечисления значениями, указанными в скобках.
Что нужно знать?
1). Перечисления в Java являются типобезопасными и имеют собственное пространство имен. Это означает, что ваше перечисление будет иметь тип, например «Валюта» в приведенном ниже примере, и вы не можете присвоить какое-либо значение, кроме указанного в константах перечисления.
2). Enum в Java – это ссылочные типы, такие как класс или интерфейс, и вы можете определить конструктор, методы и переменные внутри, что делает его более мощным, чем Enum в C и C ++, как показано в следующем примере.
3). Вы можете указать значения констант перечисления во время создания, как показано в примере ниже:
Но для этого вам необходимо определить переменную-член и конструктор, потому что PENNY (1) фактически вызывает конструктор, который принимает значение int, см. Пример ниже.
Конструктор должен быть закрытым, любой другой модификатор доступа приведет к ошибке компиляции. Теперь, чтобы получить значение, связанное с каждой монетой, вы можете определить общедоступный метод getValue() внутри перечисления Java, как любой обычный класс. Кроме того, точка с запятой в первой строке не обязательна.
4) Константы перечисления неявно статичны и окончательны и не могут быть изменены после создания. Например, приведенный ниже код приведет к ошибке компиляции:
Последнее поле EnumExamples.Currency.PENNY нельзя переназначить.
5) Может использоваться в качестве аргумента в операторе switch и с «case:», например, с примитивным типом int или char. Эта особенность делает их очень полезными для операций переключения. Давайте посмотрим, как использовать enum внутри оператора switch:
Поскольку константы, определенные внутри Enum, являются окончательными, вы можете безопасно сравнивать их, используя «==», оператор равенства, как показано в следующем примере:
Сравнение объектов с использованием оператора == не рекомендуется. Всегда используйте метод equals() или compareTo() для сравнения объектов.
7). Компилятор Java автоматически генерирует метод static values() для каждого перечисления. Метод Values() возвращает массив констант Enum в том же порядке, в котором они перечислены, и вы можете использовать values() для перебора значений, как показано в примере ниже:
8). Он также может переопределять методы. Давайте посмотрим на пример переопределения метода toString() внутри Enum, чтобы предоставить содержательное описание для констант перечислений.
А вот как это выглядит при отображении:
9). Два новых класса коллекции EnumMap и EnumSet добавлены в пакет коллекции для поддержки. Эти классы представляют собой высокопроизводительную реализацию интерфейса Map and Set в Java, и мы должны использовать их всякий раз, когда есть возможность.
EnumSet не имеет общедоступного конструктора, вместо этого он предоставляет фабричные методы для создания экземпляра, например. EnumSet.of() методы. Этот дизайн позволяет EnumSet внутренне выбирать между двумя различными реализациями в зависимости от размера констант.
Если Enum имеет менее 64 констант, чем EnumSet использует класс RegularEnumSet, который внутренне использует длинную переменную для хранения этих 64 констант, а если Enum имеет больше ключей, чем 64, то он использует JumboEnumSet.
10). Вы не можете создать экземпляр перечислений с помощью оператора new в Java, потому что конструктор может быть только частным, а константы Enums могут быть созданы только внутри него самого.
11). Экземпляр создается, когда какие-либо константы Enum сначала вызываются или упоминаются в коде.
12). Может реализовать интерфейс и переопределить любой метод, такой как обычный класс. Также стоит отметить, что Enum в Java неявно реализует как Serializable, так и Comparable интерфейс. Давайте посмотрим и пример того, как реализовать интерфейс:
13). Вы можете определить абстрактные методы внутри Enum, а также можете предоставить другую реализацию для разных экземпляров. Давайте посмотрим на пример использования абстрактного метода внутри.
В этом примере, поскольку каждая монета будет иметь разный цвет, мы сделали метод color() абстрактным и позволили каждому экземпляру Enum определять свой собственный цвет. Вы можете получить цвет любой монеты, просто вызвав метод color(), как показано в примере ниже:
Привет! Теме что такое 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’ов окончено.