Перечисления — различия между версиями
(Вся статья) |
(добавлены EnumMap и EnumSet, изменены и добавлены примеры) |
||
Строка 16: | Строка 16: | ||
== Перечисление - это класс == | == Перечисление - это класс == | ||
− | Объявляя enum, мы неявно создаем класс производный от java.lang.Enum. Условно конструкция enum | + | Объявляя enum, мы неявно создаем класс производный от java.lang.Enum. Условно конструкция enum Unit { ... } эквивалентна |
− | class | + | class Unit extends java.lang.Enum { ... }. |
И хотя явным образом наследоваться от <tex>java.lang.Enum</tex> нам не позволяет компилятор, все же в том, что <tex>enum</tex> наследуется, легко убедиться с помощью <tex>reflection:</tex> | И хотя явным образом наследоваться от <tex>java.lang.Enum</tex> нам не позволяет компилятор, все же в том, что <tex>enum</tex> наследуется, легко убедиться с помощью <tex>reflection:</tex> | ||
− | System.out.println( | + | System.out.println(Unit.class.getSuperclass()); |
На консоль будет выведено: | На консоль будет выведено: | ||
Строка 33: | Строка 33: | ||
Начнем с примера. Давайте опишем с помощью enum тип данных для хранения оценки студента: | Начнем с примера. Давайте опишем с помощью enum тип данных для хранения оценки студента: | ||
− | enum | + | enum Unit { KILOMETER, METER, MILLIMETER } |
− | Здесь мы использовали новое ключевое слово enum, присвоили ему имя и указал разрешенные значения. | + | Здесь мы использовали новое ключевое слово enum, присвоили ему имя и указал разрешенные значения. Unit стал перечислимым типом, который вы можете использовать, например, следующим способом: |
− | public class | + | public class Distance { |
− | private | + | private double dist; |
− | private | + | private Unit unit; |
− | public | + | public Distance(double dist, Unit unit) { |
− | this. | + | this.unit = unit; |
− | this. | + | this.dist = dist; |
} | } | ||
− | public void | + | public void setUnit(Unit unit) { |
− | this. | + | this.unit = unit; |
} | } | ||
− | public | + | public Unit getUnit() { |
− | return | + | return unit; |
} | } | ||
− | public void | + | public void setDistance (double dist) { |
− | this. | + | this. dist = dist; |
} | } | ||
− | public | + | public double getDistance() { |
− | return | + | 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 | ||
+ | |||
+ | Два производных класса будут созданы с полиморфным методом Object parse(String) и конструктором Unit(..., boolean). При этом объекты классов 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== | ||
Строка 65: | Строка 116: | ||
Рассмотрим пример, показывающий, как пройти по значениям любого перечислимого типа. | Рассмотрим пример, показывающий, как пройти по значениям любого перечислимого типа. | ||
− | public void | + | public void PrintAll (PrintStream out) throws IOException { |
− | for ( | + | 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)); | ||
+ | } | ||
} | } | ||
} | } | ||
Строка 73: | Строка 126: | ||
В результате чего мы получим следующий текст: | В результате чего мы получим следующий текст: | ||
− | + | 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( | + | System.out.println(Arrays.toString(Unit.values())); |
В результате будет выведено: | В результате будет выведено: | ||
− | [ | + | [KILOMETER, METER, MILLIMETER] |
===Переключение с enum=== | ===Переключение с enum=== | ||
Рассмотренные ранее примеры являются довольно простыми, но перечислимые типы предлагают значительно больше. Перечислимые значения, которые можно использовать для итерации и в операторах <tex>switch</tex>, имеют большое значение. | Рассмотренные ранее примеры являются довольно простыми, но перечислимые типы предлагают значительно больше. Перечислимые значения, которые можно использовать для итерации и в операторах <tex>switch</tex>, имеют большое значение. | ||
− | Для сравнения значения | + | Для сравнения значения unit с разными возможными мерами длин использовать следующую запись: |
− | + | unit.equals(Unit.METER) | |
Но множество сравнений заняло бы много места. | Но множество сравнений заняло бы много места. | ||
− | Для этого мы используем <tex>switch</tex>, в результате чего у нас получится: | + | Для этого мы используем <tex>switch</tex>, в результате чего у нас получится следующая запись: |
− | switch ( | + | switch (unit) { |
− | case | + | case KILOMETER: |
− | + | return "km"; | |
− | + | case METER: | |
− | + | return "m"; | |
− | + | case MILLIMETER: | |
− | + | return "mm"; | |
− | |||
− | |||
− | |||
− | case | ||
− | |||
− | |||
− | |||
− | case | ||
− | |||
− | |||
} | } | ||
− | В результате чего он выведет соответствующую оценку. Так же можно употребить <tex>default </tex>, на тот случай, если вдруг другой программист добавил новые значения в | + | В результате чего он выведет соответствующую оценку. Так же можно употребить <tex>default </tex>, на тот случай, если вдруг другой программист добавил новые значения в Unit, а вас не предупредил. Поставив для варианта <tex>default </tex> вывод сообщения об отсутствии в списке значенияя unit, вы обнаружите это изменение. |
==Получение елемента enum по строковому представлению его имени== | ==Получение елемента enum по строковому представлению его имени== | ||
Довольно часто возникает задача получить элемент enum по его строковому представлению. Для этих целей в каждом enum-классе компилятор автоматически создает специальный статический метод: public static EnumClass valueOf(String name), который возвращает элемент перечисления EnumClass с названием, равным name. Пример использования: | Довольно часто возникает задача получить элемент enum по его строковому представлению. Для этих целей в каждом enum-классе компилятор автоматически создает специальный статический метод: public static EnumClass valueOf(String name), который возвращает элемент перечисления EnumClass с названием, равным name. Пример использования: | ||
− | String name = " | + | String name = "METER"; |
− | + | Unit unit = Unit.valueOf(name); | |
− | В результате чего в | + | В результате чего в unit будет записано значение METER. |
− | == | + | ==Коллекции перечислимых типов== |
− | + | ||
− | + | В Java использование генериков в enum запрещено. Но можно использовать EnumMap и EnumSet. | |
− | + | ||
− | + | ===EnumSet=== | |
− | + | Специализированное Set реализация для использования с перечислимыми типами. Все элементы в перечислимом наборе должны прибыть из единственного перечислимого типа, который определяется, явно или неявно, когда набор создается. Перечислимые наборы представляются внутренне как битовый векторы. Это представление чрезвычайно компактно и эффективно. | |
− | |||
− | + | Нулевые элементы не разрешаются. Попытки вставить нулевой элемент бросят NullPointerException. Попытки протестировать на присутствие нулевого элемента или удалить каждый будет, однако, функционировать должным образом. | |
− | |||
− | + | EnumSet работает почти так же, как и Set. Рассмотрим методы EnumSet : | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | allOf(Class<E> elementType) - создает перечислимы набор, содержащий все эллементы в указанном типе эллементов. | |
− | + | ||
+ | clone() - Возвращает копию этого набора. | ||
+ | |||
+ | complementOf(EnumSet<E> s) - Создает перечислимый набор с тем же самым типом элемента как указанный перечислимый набор, | ||
+ | первоначально содержащий все элементы этого типа, которыене содержатся в указанном наборе. | ||
+ | |||
+ | copyOf(Collection<E> c) - Создает перечислимый набор, инициализированный из указанного набора. | ||
+ | |||
+ | copyOf(EnumSet<E> s) - Создает перечислимый набор с тем же самым типом элемента как указанный перечислимый набор, | ||
+ | первоначально содержащий те же самые элементы (если любой). | ||
+ | |||
+ | noneOf(Class<E> elementType) - Создает пустой перечислимый набор с указанным типом элемента. | ||
+ | |||
+ | of(E e1, E e2, E e3...) - Создает перечислимый набор, первоначально содержащий указанные элементы. | ||
+ | |||
+ | range(E from, E to) - Создает перечислимый набор, первоначально содержащий все элементы в диапазоне, | ||
+ | определенном двумя указанными конечными точками. | ||
+ | |||
+ | ===EnumMap=== | ||
+ | EnumMap - это специализированный класс Map, для работы с Enum. В EnumMap все происходит аналогично. Все Эллементы должны быть из единственного перечислимого типа. Эллементы так же хранятся в их естественном порядке(порядок в котором хранятся Enum константы). И Так же не доступен нулевой эллемент, попытки вставить его бросят NullPointerException. | ||
+ | |||
+ | Создание EnumMap : | ||
+ | |||
+ | EnumMap<K,V>(K.class) | ||
+ | EnumMap<K,V>(EnumMap<K,V>) | ||
+ | EnumMap<K,V>(Map<K,V>) | ||
− | + | Методы EnumMap соответствуют методам Map. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==Пример== | ==Пример== |
Версия 15:19, 30 сентября 2013
Содержание
Предыстория
Программируя, мы часто сталкиваемся с необходимостью ограничить множество допустимых значений для некоторого типа данных. Так, например, день недели может иметь 7 разных значений, месяц в году - 12, а время года - 4. Для решения подобных задач во многих языках программирования со статической типизацией предусмотрен специальный тип данных - перечисление
. Данный тип данных появился в Java начиная с версии 1.5. (enum) предоставляет множество удобств как в компактности записи, так и в удобстве использования. Так же, по сравнению, допустим, с C++, перечисления в Java представляют собой полноценные объекты, что предоставляет разработчику гораздо большую гибкость.Введение
Перечисления наследуются от класса
, у которого есть ряд удобных методов. Вот одни из них :— имя константы в виде строки
— порядок константы (соответствует порядку, в котором объявлены константы)
— статический метод, позволяющий получить объект перечисления по классу и имени
Так же, у класса перечисления есть возможность получить все возможные значения перечисления путем вызова метода
. В классе перечисления имеется возможность задавать конструкторы (только приватные), поля и методы.Перечисление - это класс
Объявляя enum, мы неявно создаем класс производный от java.lang.Enum. Условно конструкция enum Unit { ... } эквивалентна
class Unit extends java.lang.Enum { ... }.
И хотя явным образом наследоваться от
нам не позволяет компилятор, все же в том, что наследуется, легко убедиться с помощьюSystem.out.println(Unit.class.getSuperclass());
На консоль будет выведено:
class java.lang.Enum
Собственно наследование за нас автоматически выполняет компилятор Java.
Конструкция enum
Начнем с примера. Давайте опишем с помощью enum тип данных для хранения оценки студента:
enum Unit { KILOMETER, METER, MILLIMETER }
Здесь мы использовали новое ключевое слово 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
Два производных класса будут созданы с полиморфным методом Object parse(String) и конструктором Unit(..., boolean). При этом объекты классов 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
Рассмотренные ранее примеры являются довольно простыми, но перечислимые типы предлагают значительно больше. Перечислимые значения, которые можно использовать для итерации и в операторах
, имеют большое значение.Для сравнения значения unit с разными возможными мерами длин использовать следующую запись:
unit.equals(Unit.METER)
Но множество сравнений заняло бы много места. Для этого мы используем
, в результате чего у нас получится следующая запись:switch (unit) { case KILOMETER: return "km"; case METER: return "m"; case MILLIMETER: return "mm"; }
В результате чего он выведет соответствующую оценку. Так же можно употребить
, на тот случай, если вдруг другой программист добавил новые значения в Unit, а вас не предупредил. Поставив для варианта вывод сообщения об отсутствии в списке значенияя unit, вы обнаружите это изменение.Получение елемента enum по строковому представлению его имени
Довольно часто возникает задача получить элемент enum по его строковому представлению. Для этих целей в каждом enum-классе компилятор автоматически создает специальный статический метод: public static EnumClass valueOf(String name), который возвращает элемент перечисления EnumClass с названием, равным name. Пример использования:
String name = "METER"; Unit unit = Unit.valueOf(name);
В результате чего в unit будет записано значение METER.
Коллекции перечислимых типов
В Java использование генериков в enum запрещено. Но можно использовать EnumMap и EnumSet.
EnumSet
Специализированное Set реализация для использования с перечислимыми типами. Все элементы в перечислимом наборе должны прибыть из единственного перечислимого типа, который определяется, явно или неявно, когда набор создается. Перечислимые наборы представляются внутренне как битовый векторы. Это представление чрезвычайно компактно и эффективно.
Нулевые элементы не разрешаются. Попытки вставить нулевой элемент бросят NullPointerException. Попытки протестировать на присутствие нулевого элемента или удалить каждый будет, однако, функционировать должным образом.
EnumSet работает почти так же, как и Set. Рассмотрим методы EnumSet :
allOf(Class<E> elementType) - создает перечислимы набор, содержащий все эллементы в указанном типе эллементов.
clone() - Возвращает копию этого набора.
complementOf(EnumSet<E> s) - Создает перечислимый набор с тем же самым типом элемента как указанный перечислимый набор, первоначально содержащий все элементы этого типа, которыене содержатся в указанном наборе.
copyOf(Collection<E> c) - Создает перечислимый набор, инициализированный из указанного набора.
copyOf(EnumSet<E> s) - Создает перечислимый набор с тем же самым типом элемента как указанный перечислимый набор, первоначально содержащий те же самые элементы (если любой).
noneOf(Class<E> elementType) - Создает пустой перечислимый набор с указанным типом элемента.
of(E e1, E e2, E e3...) - Создает перечислимый набор, первоначально содержащий указанные элементы.
range(E from, E to) - Создает перечислимый набор, первоначально содержащий все элементы в диапазоне, определенном двумя указанными конечными точками.
EnumMap
EnumMap - это специализированный класс Map, для работы с Enum. В EnumMap все происходит аналогично. Все Эллементы должны быть из единственного перечислимого типа. Эллементы так же хранятся в их естественном порядке(порядок в котором хранятся Enum константы). И Так же не доступен нулевой эллемент, попытки вставить его бросят NullPointerException.
Создание EnumMap :
EnumMap<K,V>(K.class) EnumMap<K,V>(EnumMap<K,V>) EnumMap<K,V>(Map<K,V>)
Методы 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); }