Изменения

Перейти к: навигация, поиск

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

10 746 байт добавлено, 00:17, 23 марта 2021
Наследование в enum
==ПредисторияПредыстория==Программируя , мы часто сталкиваемся с необходимостью ограничить множество допустимых значений для некоторого типа данных. Так, например, день недели может иметь 7 разных значений, месяц в году - 12, а время года - 4. Для решения подобных задач во многих языках программирования со статической типизацией предусмотрен специальный тип данных - перечисление <tex>(enum)</tex>.Данный тип данных появился в Java начиная с версии 1.5. (enum) предоставляет множество удобств как в компактности записи, так и в удобстве использования. Так же, в отличие, допустим, от C, перечисления в Java представляют собой полноценные объекты, что предоставляет разработчику гораздо большую гибкость. 
==Введение ==
В <tex>Java</tex>, начиная с версии 1.5, помимо всего прочего появились так называемые перечисления <tex>(enum)</tex>. Существует целый ряд плюсов от использования перечислений против именованных констант:Компилятор гарантирует корректную проверку типовУдобство итерации по всем возможным значениям перечисленияОни занимают меньше места в <tex>switch-</tex>блоке (не нужно указывать имя класса)и т.д. Однако по сравнению, допустим, с C++, перечисления в <tex>Java</tex> представляют собой полноценные объекты, что предоставляет разработчику гораздо большую гибкость.Во-первых, все перечисления Перечисления наследуются от класса <tex>java.lang.Enum</tex>, у которого есть ряд удобных методов, а именно. Вот одни из них :
<tex>— name()</tex> — имя константы в виде строки
<tex>— valueOf()</tex> — статический метод, позволяющий получить объект перечисления по классу и имени
Далее, как уже было озвученоТак же, у класса перечисления есть возможность получить все возможные значения перечисления путем вызова метода <tex>java.lang.Class.getEnumConstants()</tex> у класса перечисления.В классе перечисления имеется возможность задавать конструкторы (только приватные), поля и методы. == Перечисление - это класс ==Объявляя enum, мы неявно создаем класс производный от java.lang.Enum. Условно конструкция enum Unit { ... } эквивалентна   class Unit extends java.lang.Enum { ... }.  И хотя явным образом наследоваться от <tex>java.lang.Enum</tex> нам не позволяет компилятор, все же в том, что <tex>enum</tex> наследуется, легко убедиться с помощью <tex>reflection:</tex>  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 Объекты классов 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===Перечислимые типы очень удобно использовать для итерации и в операторах <tex>switch</tex>. Для сравнения значения unit с разными возможными мерами длин можно использовать следующую запись:  unit.equals(Unit.METER) Но множество сравнений заняло бы много места.Для этого мы используем <tex>switch</tex>, в результате чего у нас получается следующая запись:  switch (unit) { case KILOMETER: return "km"; case METER: return "m"; case MILLIMETER: return "mm"; } В результате чего он выдаст соответствующую меру длины. Так же можно употребить <tex>default</tex>, на тот случай, если вдруг другой программист добавил новые значения в Unit, а вас не предупредил. Поставив для варианта <tex>default</tex> вывод сообщения об отсутствии в списке значения 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<T> EnumSet.allOf(T.class) - создает EnumSet, содержащий все элементы из указанного класса.  EnumSet<T> EnumSet.copyOf(Collection<T> t) - Создает EnumSet, содержащий все элементы находящиеся в колекции.  EnumSet<T> EnumSet.copyOf(EnumSet<T> s) - Создает EnumSet содержащий те же элементы.  EnumSet<T> EnumSet.complementOf(EnumSet<T> s) - Создает EnumSet с тем же типом данных каки у указанного EnumSet, впрочемно содержащий элементы не входящие в указанный набор.  EnumSet<T> EnumSet.noneOf(T.class) - Создает пустой EnumSet, но сразу определяет для него тип используемых в нем данных в последующей работе с ним.  EnumSet<T> EnumSet.of(e1, e2, e3...) - Создает EnumSet, первоначально содержащий указанные элементы.  EnumSet<T> EnumSet.range(from, to) - Создает EnumSet, первоначально содержащий все элементы в диапазоне от первого указанного элемента до второго указанного элемента. ===EnumMap===EnumMap - это специализированный класс Map, для работы с Enum. В EnumMap все происходит аналогично. Все Эллементы должны быть из единственного перечислимого типа. Эллементы так же хранятся в их естественном порядке(порядок в котором хранятся элементы в Enum классе). И Так же не доступен нулевой эллемент, попытки вставить его бросят Exception.  Рассмотрим примеры создания EnumMap :  EnumMap<K,V>(K.class) - создает пустой EnumMap, и переопределять уже определенныеопределяет для него тип хранящихся ключей. EnumMap<K,V>(EnumMap<K,V>) - создает EnumMap являющийся копией данного. EnumMap<K,V>(Map<K,V>) - создает EnumMap, инициализируемую по данной Map. ==Пример==Раньше класс бинарных операций мы записывали следующим образом:
== Пример ==
Раньше класс бинарные операции мы делали вот так
public class BinaryOperation {
public class Plus {
}
}
Проблема была в том, что пригодилось делать много проверок извне для вызова этих функции. Можно сделать гораздо проще и удобнееС использованием enum все значительно упрощается: 
public enum BinaryOperation {
Plus("+") {
}
abstract public int calculate(int a, int b);
}
Анонимный участник

Навигация