Перейти к содержимому

Enum java что это

  • автор:

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

В языке Java, как и во многих других языках имеется конструкция под названием перечисления. Но, в отличие от множества других языков, перечисления реализованы в виде классов и это открывает дополнительные возможности их использования.

Итак, что же такое перечисление и зачем они нужны? В самом простом случае – это список констант для общего использования в рамках проекта. Например, это могут быть коды ошибок, или номера клавиш, или типы геометрических фигур и т.д. Главное, что с помощью перечислений достаточно удобно объявлять группы констант, логически (по смыслу) связанных между собой.

Чтобы задать перечисления в Java используется следующий синтаксис:

enum <
// константы и другие элементы перечисления
>

Например, зададим через enum набор констант для разных типов геометрических фигур:

enum GeomType { POINT, LINE, TRIANGLE, RECTANGLE, ELLIPSE; }

Здесь указанные элементы через запятую представляют собой константы перечисляемого типа с неявным набором модификаторов

public static final

Например, POINT – это публичная статическая константа типа GeomType. И мы легко можем в этом убедиться, выполнив в методе main следующую строчку:

System.out.println(GeomType.POINT.getClass());

В консоли увидим:

Как же пользоваться этими константами? Предположим, у нас есть базовый класс Geom и дочерние классы Point и Line. Запишем их в виде:

abstract class Geom { public GeomType type; } class Point extends Geom { Point(GeomType type) { this.type = type; } void getPointMethod() { System.out.println("Класс Point"); } } class Line extends Geom { Line(GeomType type) { this.type = type; } void getLineMethod() { System.out.println("Класс Line"); } }

Затем, в методе main создадим несколько экземпляров этих классов с указанием их типа:

public class Main { public static void main(String[] args) { final int N = 3; Geom g[] = new Geom[N]; g[0] = new Line(GeomType.LINE); g[1] = new Point(GeomType.POINT); g[2] = new Line(GeomType.LINE); } }

И, далее, для приведения обобщенных ссылок g к соответствующим дочерним классам, мы можем прописать следующую проверку:

for(Geom fig: g) switch (fig.type) { case POINT: ((Point)fig).getPointMethod(); break; case LINE: ((Line)fig).getLineMethod(); break; }

Причем, в условии оператора switch достаточно указать имя константы без имени самого перечисления. Виртуальная машина Java автоматически определит, что идет проверка на константу POINT (или LINE) перечисления GeomType, так как поле fig.type имеет такой тип.

Так как само по себе перечисление – это класс, то оно может содержать различные методы и константы. В частности, в любом перечислении имеется метод values(), который возвращает массив констант. Например, строчки:

for(GeomType type: GeomType.values()) System.out.println(type);

выведут в консоль список всех констант, определенных в перечислении GeomType.

Другой метод ordinal() возвращает порядковый номер константы:

for(GeomType type: GeomType.values()) System.out.println(type + " = " + type.ordinal());

С помощью метода equals() выполняется сравнение двух констант одного и того же перечисления:

GeomType pt = GeomType.POINT; if(GeomType.POINT.equals(pt)) System.out.println("Константы равны");

Или, можно использовать более общий метод compareTo(), который возвращает значения > 0, 0, < 0 как показано в следующем примере:

if(GeomType.LINE.compareTo(pt) > 0) System.out.println("Константа LINE больше pt"); GeomType line = GeomType.LINE; if(GeomType.POINT.compareTo(line)  0) System.out.println("Константа POINT меньше line"); if(GeomType.POINT.compareTo(pt) == 0) System.out.println("Константы POINT и pt равны");

Все эти примера показывают, что сами по себе константы в перечислении – это объекты, у которых есть свои методы. Но раз это объекты, то можно ли им указывать свои конструкторы и добавлять свои методы и поля? Оказывается да и делается это следующим образом:

enum GeomType { POINT("point"), LINE("line"), TRIANGLE("triangle"); private String value; GeomType(String value) { this.value = value; } public String getValue() { return value; } }

Смотрите, мы здесь добавили конструктор для перечисления GeomType и, так как константы – это объекты этого перечисления, то теперь, при их определении мы должны указать этот строковый параметр. Соответственно, строка для каждого объекта будет храниться в приватной переменной value, которую можно получить с помощью метода getValue():

public class Main { public static void main(String[] args) { final int N = 3; Geom g[] = new Geom[N]; g[0] = new Line(GeomType.LINE); g[1] = new Point(GeomType.POINT); g[2] = new Line(GeomType.LINE); for(Geom fig: g) { String value = fig.type.getValue(); System.out.println(value); } } }

Обратите внимание, что конструктор перечисления всегда приватный, то есть, мы можем его использовать только внутри перечисления для создания объектов-констант. За пределами перечисления этот конструктор будет недоступен. Также следует знать, что при создании константы происходит автоматическое наследование от класса Enum – общего для всех перечислений. Соответственно, объекты-константы содержат все общедоступные методы перечислений.

Видео по теме

#11 Концепция объектно-ориентированного программирования (ООП)

#12 Классы и создание объектов классов

#13 Конструкторы, ключевое слово this, инициализаторы

#14 Методы класса, сеттеры и геттеры, public, private, protected

#15 Пакеты, модификаторы конструкторов и классов

#16 Ключевые слова static и final

#17 Внутренние и вложенные классы

#18 Как делается наследование классов

#19 Ключевое слово super, оператор instanceof

#20 Модификаторы private и protected, переопределение методов, полиморфизм

#21 Абстрактные классы и методы

#22 Интерфейсы — объявление и применение

#23 Интерфейсы — приватные, статические и дефолтные методы, наследование интерфейсов

#24 Анонимные внутренние классы

#25 Перечисления (enum)

#26 Обобщения классов (Generics)

#27 Ограничения типов, метасимвольные аргументы, обобщенные методы и конструкторы

#28 Обобщенные интерфейсы, наследование обобщенных классов

© 2024 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта

Enum java что это

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

enum Day

Перечисление фактически представляет новый тип, поэтому мы можем определить переменную данного типа и использовать ее:

public class Program < public static void main(String[] args) < Day current = Day.MONDAY; System.out.println(current); // MONDAY >> enum Day

Перечисления могут использоваться в классах для хранения данных:

public class Program < public static void main(String[] args) < Book b1 = new Book("War and Peace", "L. Tolstoy", Type.BELLETRE); System.out.printf("Book '%s' has a type %s", b1.name, b1.bookType); switch(b1.bookType)< case BELLETRE: System.out.println("Belletre"); break; case SCIENCE: System.out.println("Science"); break; case SCIENCE_FICTION: System.out.println("Science fiction"); break; case PHANTASY: System.out.println("Phantasy"); break; >> > class Book < String name; Type bookType; String author; Book(String name, String author, Type type)< bookType = type; this.name = name; this.author = author; >> enum Type

Само перечисление объявлено вне класса, оно содержит четыре жанра книг. Класс Book кроме обычных переменных содержит также переменную типа нашего перечисления. В конструкторе мы ее также можем присвоить, как и обычные поля класса.

С помощью конструкции switch..case можно проверить принадлежность значения bookType определенной константе перечисления.

Методы перечислений

Каждое перечисление имеет статический метод values() . Он возвращает массив всех констант перечисления:

public class Program < public static void main(String[] args) < Type[] types = Type.values(); for (Type s : types) < System.out.println(s); >> > enum Type

Метод ordinal() возвращает порядковый номер определенной константы (нумерация начинается с 0):

System.out.println(Type.BELLETRE.ordinal()); // 1

Конструкторы, поля и методы перечисления

Перечисления, как и обычные классы, могут определять конструкторы, поля и методы. Например:

public class Program < public static void main(String[] args) < System.out.println(Color.RED.getCode()); // #FF0000 System.out.println(Color.GREEN.getCode()); // #00FF00 >> enum Color < RED("#FF0000"), BLUE("#0000FF"), GREEN("#00FF00"); private String code; Color(String code)< this.code = code; >public String getCode() < return code;>>

Перечисление Color определяет приватное поле code для хранения кода цвета, а с помощью метода getCode оно возвращается. Через конструктор передается для него значение. Следует отметить, что конструктор по умолчанию приватный, то есть имеет модификатор private. Любой другой модификатор будет считаться ошибкой. Поэтому создать константы перечисления с помощью конструктора мы можем только внутри перечисления.

Также можно определять методы для отдельных констант:

public class Program < public static void main(String[] args) < Operation op = Operation.SUM; System.out.println(op.action(10, 4)); // 14 op = Operation.MULTIPLY; System.out.println(op.action(6, 4)); // 24 >> enum Operation < SUM< public int action(int x, int y)< return x + y;>>, SUBTRACT < public int action(int x, int y)< return x - y;>>, MULTIPLY < public int action(int x, int y)< return x * y;>>; public abstract int action(int x, int y); >

Перечисления (enums)

Иногда нужно описать ограниченное множество элементов. Встарь это делали посредством целочисленных констант:

public final class HairColourInt < // не позволим никому создавать экземпляры private HairColourInt() <>public static final int BLONDE = 0; public static final int FAIR = 1; public static final int RED = 2; public static final int BROWN = 3; public static final int BLACK = 4; >

Минусов у такого подхода несколько:

  • Можно, добавляя в новую константу, по ошибке использовать старое числовое значение, например, добавить GREEN = 4 , в то время как BLACK тоже = 4 .
  • int приводится к строке как число, это неудобно для отладки.
  • В метод, который ожидает на вход значение такой константы, можно передать произвольное число. Например, здесь мы используем числа от нуля до четырёх, но можно передать хоть -1 , хоть 2_000_000_000 .

Enum

enum — это вид класса. У него есть ограниченный набор элементов, и все они доступны статически.

Другие названия: enumerated type, перечисляемый тип, перечисление.

public enum HairColour

Если объявить метод, скажем, findPersonWith(HairColour colour) , то все значения, которые можно передать в метод, ограничиваются следующими: BLONDE , FAIR , RED , BROWN , BLACK и null .

Person redhead = findPersonWith(HairColour.RED);

Стандартные методы enum’ов

Все enum’ы являются наследниками класса java.lang.Enum и обладают следующими instance-методами:

name() Имя константы. HairColour.BROWN.name() == «BROWN» ordinal() Порядковый номер. HairColor.BLONDE.ordinal() == 0

Также компилятор генерирует для каждого enum-класса следующие статические методы:

valueOf(String name) Находит константу по имени. HairColour.valueOf(«BROWN») == HairColour.BROWN; HairColour.valueOf(«YELLOW») бросит IllegalArgumentException, т. к. константы с таким именем нет values() Массив допустимых значений. HairColor.values() == [BLONDE, FAIR, RED, BROWN, BLACK]

Стоит учитывать, что, т. к. в Java массивы изменяемы, метод values() каждый раз возвращает новую копию массива (производит защитное копирование). Подробнее об экономии памяти в этой ситуации — в статье «Enum в Java. Маленькая недоработка».

Добавление собственных полей и методов

Очень часто нужно добавить какую-нибудь информацию к элементам enum’а. Это можно сделать так:

public enum HairColour < // Пусть у каждого цвета волос // будет степень яркости. BLONDE(1), FAIR(.8f), RED(.7f), BROWN(.4f), BLACK(0); // Поле для хранения данных. // Должно быть финальным: // недопустимо, чтобы у элемента перечисления // было состояние, т. к. он — константа. private final float brightness; // Конструктор. Он приватный, // изменить это нельзя. HairColour(float brightness) < this.brightness = brightness; >// геттер для поля public float getBrightness() < return brightness; >// Метод для поиска цвета // по уровню яркости. public static HairColour withBrightness(float brightness) < // обходим все возможные значения for (HairColour colour : values()) < // если у очередного цвета // подходящий уровень яркости — возвращаем if (colour.brightness == brightness) < return colour; >> // если нужный цвет не найден — // выбрасываем исключение throw new NoSuchElementException( "There's no HairColour with brightness " + brightness); > >

В этом примере HairColour.RED.getBrightness() == .7f , HairColour.withBrightness(0) == HairColour.BLACK , а HairColour.withBrightness(.003f) бросит исключение NoSuchElementException .

Реализация интерфейсов

Enum’ы могут реализовывать интерфейсы.

public enum HairColour implements Runnable < . @Override public void run() < System.out.println("person with " + name().toLowerCase() + " hair runs"); >>

HairColour.FAIR.run() выведет person with fair hair runs .

Собственные методы элементов enum’а

Отдельные элементы перечисления могут быть анонимными классами и иметь собственные методы. Например:

public enum HairColour implements Runnable < BLONDE(1), FAIR(.8f), RED(.7f) < // тело анонимного класса @Override public boolean hasSoul() < return false; >>, BROWN(.4f), BLACK(0); . public boolean hasSoul() < return true; >>

Таким образом, enum — это не просто набор констант, это почти полноценный класс и очень мощный инструмент.

Перечисления

Программируя, мы часто сталкиваемся с необходимостью ограничить множество допустимых значений для некоторого типа данных. Так, например, день недели может иметь 7 разных значений, месяц в году — 12, а время года — 4. Для решения подобных задач во многих языках программирования со статической типизацией предусмотрен специальный тип данных — перечисление [math](enum)[/math] . Данный тип данных появился в Java начиная с версии 1.5. (enum) предоставляет множество удобств как в компактности записи, так и в удобстве использования. Так же, в отличие, допустим, от C, перечисления в Java представляют собой полноценные объекты, что предоставляет разработчику гораздо большую гибкость.

Введение

Перечисления наследуются от класса [math]java.lang.Enum[/math] , у которого есть ряд удобных методов. Вот одни из них :

[math]— name()[/math] — имя константы в виде строки

[math]— ordinal()[/math] — порядок константы (соответствует порядку, в котором объявлены константы)

[math]— valueOf()[/math] — статический метод, позволяющий получить объект перечисления по классу и имени

Так же, у класса перечисления есть возможность получить все возможные значения перечисления путем вызова метода [math]java.lang.Class.getEnumConstants()[/math] . В классе перечисления имеется возможность задавать конструкторы (только приватные), поля и методы.

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

Объявляя enum, мы неявно создаем класс производный от java.lang.Enum. Условно конструкция enum Unit < . >эквивалентна

class Unit extends java.lang.Enum < . >.

И хотя явным образом наследоваться от [math]java.lang.Enum[/math] нам не позволяет компилятор, все же в том, что [math]enum[/math] наследуется, легко убедиться с помощью [math]reflection:[/math]

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

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

class java.lang.Enum

Собственно наследование за нас автоматически выполняет компилятор Java.

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

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

enum Unit

Здесь мы использовали новое ключевое слово enum, присвоили ему имя и указал разрешенные значения. Unit стал перечислимым типом, который вы можете использовать, например, следующим способом:

public class Distance < private double dist; private Unit unit; public Distance(double dist, Unit unit) < this.unit = unit; this.dist = dist; >public void setUnit(Unit unit) < this.unit = unit; >public Unit getUnit() < return unit; >public void setDistance (double dist) < this. dist = dist; >public double getDistance() < return dist; >>

Создав новое перечисление (Unit) предварительно определенного типа, вы можете использовать его аналогично любой другой переменной экземпляра. Естественно, перечислению можно присвоить только одно из перечисленных значений. Обратите внимание также на то, что в getUnit() нет кода проверки ошибок на выход за пределы границ.

Добавление методов в enum

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

enum Unit < KILOMETER(1e3) METER(1) MILLIMETER(1e-3) private final double length; private Unit(double length) < this.length = length >public double getLength() < return length; >>

Обратите внимание что использование конструкторов в enum так же доступно, как и в других классах.

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

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

enum Unit < KILOMETER < public double getLength() < return 1000; >>, METER < public double getLength() < return 1; >>, MILLIMETER < public double getLength() < return 0.001; >>; public abstract double getLength(); >

Здесь объявляется перечисление Unit с тремя элементами KILOMETER, METER и MILLIMETER. Компилятор создаст следующие классы и объекты:

•Unit - класс производный от java.lang.Enum •KILOMETER - объект 1-го класса производного от Unit •METER - объект 2-го класса производного от Unit •MILLIMETER - объект 3-го класса производного от Unit

Объекты классов KILLOMETER, MILLIMETER и METER существуют в единственном экземпляре и доступны статически. В этом можно убедится:

System.out.println(Unit.class); System.out.println(Unit.KILOMETER.getClass() + " " + Unit.KILOMETER.getClass().getSuperclass()); System.out.println(Unit.METER.getClass() + " " + Unit.METER.getClass().getSuperclass()); System.out.println(Unit.MILLIMETER.getClass() + " " + Unit.MILLIMETER.getClass().getSuperclass());

Результат будет следующим:

class Unit class Unit$1 class Unit class Unit$2 class Unit class Unit$3 class Unit

Использование enum

Итерация по enum

Рассмотрим пример, показывающий, как пройти по значениям любого перечислимого типа.

public void PrintAll (PrintStream out) throws IOException < for (Unit u1 : Unit.values()) < for (Unit u2 : Unit.values()) < out.println(String.format("There are %f %sS in one %s",u2.getLength()/u1.getLength(), u1, u2)); >> >

В результате чего мы получим следующий текст:

There are 1 KILOMETER in one KILIMETER There are 0.001 KILOMETER in one METER There are 0.000001 KILOMETER in one MILLIMETER There are 1000 METER in one KILIMETER There are 1 METER in one METER There are 0.001 METER in one MILLIMETER There are 1000000 MILLIMETER in one KILIMETER There are 1000 MILLIMETER in one METER There are 1 MILLIMETER in one MILLIMETER

Так же можно использовать следующую запись для вывода значений на экран:

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

В результате будет выведено:

[KILOMETER, METER, MILLIMETER]

Переключение с enum

Перечислимые типы очень удобно использовать для итерации и в операторах [math]switch[/math] .

Для сравнения значения unit с разными возможными мерами длин можно использовать следующую запись:

unit.equals(Unit.METER)

Но множество сравнений заняло бы много места. Для этого мы используем [math]switch[/math] , в результате чего у нас получается следующая запись:

switch (unit)

В результате чего он выдаст соответствующую меру длины. Так же можно употребить [math]default[/math] , на тот случай, если вдруг другой программист добавил новые значения в Unit, а вас не предупредил. Поставив для варианта [math]default[/math] вывод сообщения об отсутствии в списке значения unit, вы обнаружите это изменение.

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

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

String name = "METER"; Unit unit = Unit.valueOf(name);

В результате чего в unit будет записано значение METER.

Коллекции перечислимых типов

В Java использование generics в enum запрещено. Но можно использовать EnumMap и EnumSet.

EnumSet

EnumSet — класс схожий с Set, и созданный для использования в нем элементов Enum классов. Естественно все эллементы находящиеся в EnumSet, должны быть из единственного перечислимого типа, который определяется, явно или неявно, когда набор создается. В EnumSet нулевые элементы не разрешаются, а попытки вставить нулевой элемент бросят Exception. Но, к сожалению, проверки на наличие нулевого элемента будут работать должным образом.

Как и говорилось EnumSet очень схож с обычным Set, поэтому достаточно рассмотреть только примеры инициализации EnumSet:

EnumSet EnumSet.allOf(T.class) - создает EnumSet, содержащий все элементы из указанного класса.
EnumSet EnumSet.copyOf(Collection t) - Создает EnumSet, содержащий все элементы находящиеся в колекции.
EnumSet EnumSet.copyOf(EnumSet s) - Создает EnumSet содержащий те же элементы.
EnumSet EnumSet.complementOf(EnumSet s) - Создает EnumSet с тем же типом данных как и у указанного EnumSet, но содержащий элементы не входящие в указанный набор.
EnumSet EnumSet.noneOf(T.class) - Создает пустой EnumSet, но сразу определяет для него тип используемых в нем данных в последующей работе с ним.
EnumSet EnumSet.of(e1, e2, e3. ) - Создает EnumSet, первоначально содержащий указанные элементы.
EnumSet EnumSet.range(from, to) - Создает EnumSet, первоначально содержащий все элементы в диапазоне от первого указанного элемента до второго указанного элемента.

EnumMap

EnumMap — это специализированный класс Map, для работы с Enum. В EnumMap все происходит аналогично. Все Эллементы должны быть из единственного перечислимого типа. Эллементы так же хранятся в их естественном порядке(порядок в котором хранятся элементы в Enum классе). И Так же не доступен нулевой эллемент, попытки вставить его бросят Exception.

Рассмотрим примеры создания EnumMap :

EnumMap(K.class) - создает пустой EnumMap, и определяет для него тип хранящихся ключей. EnumMap(EnumMap) - создает EnumMap являющийся копией данного. EnumMap(Map) - создает EnumMap, инициализируемую по данной Map.

Пример

Раньше класс бинарных операций мы записывали следующим образом:

public class BinaryOperation < public class Plus < public int calculate (int a, int b)< return a + b; >> public class Minus < public int calculate (int a, int b)< return a - b; >> public class Division < public int calculate (int a, int b)< return a / b; >> public class Times < public int calculate (int a, int b)< return a * b; >> >

С использованием enum все значительно упрощается:

public enum BinaryOperation < Plus("+") < public int calculate(int a, int b)< return a + b; >> Minus("-") < public int calculate(int a, int b)< return a - b; >> Division("/") < public int calculate(int a, int b)< return a / b; >> Times("*") < public int calculate(int a, int b)< return a * b; >> > abstract public int calculate(int a, int b); >

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *