Изменения

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

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

3750 байт добавлено, 00:17, 23 марта 2021
Наследование в enum
==Предыстория==
Программируя, мы часто сталкиваемся с необходимостью ограничить множество допустимых значений для некоторого типа данных. Так, например, день недели может иметь 7 разных значений, месяц в году - 12, а время года - 4. Для решения подобных задач во многих языках программирования со статической типизацией предусмотрен специальный тип данных - перечисление <tex>(enum)</tex>.
Данный тип данных появился в Java начиная с версии 1.5. (enum) предоставляет множество удобств как в компактности записи, так и в удобстве использования. Так же, по сравнениюв отличие, допустим, с от C++, перечисления в Java представляют собой полноценные объекты, что предоставляет разработчику гораздо большую гибкость.
==Введение ==
== Перечисление - это класс ==
Объявляя enum, мы неявно создаем класс производный от java.lang.Enum. Условно конструкция enum Mark Unit { ... } эквивалентна
class Mark Unit extends java.lang.Enum { ... }.
И хотя явным образом наследоваться от <tex>java.lang.Enum</tex> нам не позволяет компилятор, все же в том, что <tex>enum</tex> наследуется, легко убедиться с помощью <tex>reflection:</tex>
System.out.println(MarkUnit.class.getSuperclass());
На консоль будет выведено:
==Конструкция enum==
Начнем с примера. Давайте опишем с помощью enum тип данных для хранения оценки студентамер длины:
enum Mark Unit { AKILOMETER, BMETER, C, D, E, F MILLIMETER }
Здесь мы использовали новое ключевое слово enum, присвоили ему имя и указал разрешенные значения. Grade Unit стал перечислимым типом, который вы можете использовать, например, следующим способом:
public class Student Distance { private String Namedouble dist; private Mark markUnit unit; public StudentDistance(String Namedouble dist, Mark markUnit unit) { this.Name unit = Nameunit; this.mark dist = markdist;
}
public void setNamesetUnit(String NameUnit unit) { this.Name unit = Nameunit;
}
public String getNameUnit getUnit() { return Nameunit;
}
public void setMarksetDistance (Mark markdouble dist) { this.mark dist = markdist;
}
public Mark getMarkdouble getDistance() { return markdist;
}
}
Создав новое перечисление (MarkUnit) предварительно определенного типа, вы можете использовать его аналогично любой другой переменной экземпляра. Естественно, перечислению можно присвоить только одно из перечисленных значений (например, A, C, или F). Обратите внимание также на то, что в getMarkgetUnit() нет кода проверки ошибок на выход за пределы границ. ==Добавление методов в 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==
Рассмотрим пример, показывающий, как пройти по значениям любого перечислимого типа.
public void PrintAllMarksPrintAll (PrintStream out) throws IOException { for (Mark mark Unit u1 : MarkUnit.values()) { for (Unit u2 : Unit.values()) { out.println(String.format("Allowed marks : There are %f %sS in one %s" + mark,u2.getLength()/u1.getLength(), u1, u2)); }
}
}
В результате чего мы получим следующий текст:
Allowed marks : AThere are 1 KILOMETER in one KILIMETER Allowed marks : BThere are 0.001 KILOMETER in one METER Allowed marks : CThere are 0.000001 KILOMETER in one MILLIMETER Allowed marks : DThere are 1000 METER in one KILIMETER Allowed marks : EThere are 1 METER in one METER Allowed marks : FThere 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(MarkUnit.values()));
В результате будет выведено:
[AKILOMETER, BMETER, C, D, E, FMILLIMETER]
===Переключение с enum===
Рассмотренные ранее примеры являются довольно простыми, но перечислимые Перечислимые типы предлагают значительно больше. Перечислимые значения, которые можно очень удобно использовать для итерации и в операторах <tex>switch</tex>, имеют большое значение.
Для сравнения значения mark unit с разными возможными оценками мерами длин можно было использовать следующую запись:
markunit.equals(MarkUnit.AMETER)
Но множество сравнений заняло бы много места.
Для этого мы используем <tex>switch</tex>, в результате чего у нас получитсяполучается следующая запись:
switch (markunit) { case AKILOMETER: outputText.append(return "Great Mark Akm"); break; case B: // fall through to C case C: outputText.append ("Good Mark ") .append(mark.toString()); break; case D: // fall through to E case EMETER: outputText.append(return "Bad Mark m") .append(student1.getGrade().toString()); break; case FMILLIMETER: outputText.append(return "Negative Mark Fmm"); break;
}
В результате чего он выведет выдаст соответствующую оценкумеру длины. Так же можно употребить <tex>default </tex>, на тот случай, если вдруг другой программист добавил новые значения в MarkUnit, а вас не предупредил. Поставив для варианта <tex>default </tex> вывод сообщения об отсутствии в списке значенияя markзначения unit, вы обнаружите это изменение.
===Получение елемента элемента enum по строковому представлению его имени===
Довольно часто возникает задача получить элемент enum по его строковому представлению. Для этих целей в каждом enum-классе компилятор автоматически создает специальный статический метод: public static EnumClass valueOf(String name), который возвращает элемент перечисления EnumClass с названием, равным name. Пример использования:
String name = "BMETER"; Mark mark Unit unit = MarkUnit.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, но сразу определяет для него тип используемых в нем данных в последующей работе с ним.
В результате чего в mark будет записано значение B EnumSet<T> EnumSet.of(e1, e2, e3...) - Создает EnumSet, первоначально содержащий указанные элементы.
==Добавление методов в enum==У Вас есть возможность добавлять собственные методы как в enum EnumSet<T> EnumSet.range(from, to) -классСоздает EnumSet, так и первоначально содержащий все элементы в его элементы: enum MarkBool { GOOD, BAD; public MarkBool opposite() { return this == GOOD ? BAD : GOOD; }диапазоне от первого указанного элемента }до второго указанного элемента.
==Наследование в enum=EnumMap===С помощью enum EnumMap - это специализированный класс Map, для работы с Enum. В EnumMap все происходит аналогично. Все Эллементы должны быть из единственного перечислимого типа. Эллементы так же хранятся в Java можно реализовать иерархию классов, объекты которой создаются их естественном порядке(порядок в котором хранятся элементы в единственном экземпляре и доступны статическиEnum классе). При этом элементы enum могут содержать собственные конструкторыИ Так же не доступен нулевой эллемент, попытки вставить его бросят Exception.
enum MarkBool { GOOD { public MarkBool opposite() { return BAD; } }, BAD { public MarkBool opposite() { return GOOD; } }; public abstract MarkBool opposite(); }Рассмотрим примеры создания EnumMap :
Здесь объявляется перечисление MarkBool с двумя элементами BAD и GOOD. Компилятор создаст следующие классы и объекты: •MarkBool - класс производный от javaEnumMap<K,V>(K.lang.Enum •BAD class) - объект 1-го класса производного от MarkBool •GOOD - объект 2-го класса производного от MarkBoolДва производных класса будут созданы с полиморфным методом Object parse(String) и конструктором MarkBool(...создает пустой EnumMap, boolean) При этом объекты классов BAD и GOOD существуют в единственном экземпляре и доступны статическиопределяет для него тип хранящихся ключей. В этом можно убедится: System.out.printlnEnumMap<K,V>(MarkBool.classEnumMap<K,V>); System- создает EnumMap являющийся копией данного.out.println(MarkBool.BAD.getClass() + " " + MarkBool.BAD.getClass().getSuperclass()); System.out.printlnEnumMap<K,V>(MarkBool.GOOD.getClass() + " " + MarkBool.GOOD.getClass().getSuperclass()Map<K,V>);Результат будет следующим: class MarkBool- class MarkBool $1 class MarkBool class MarkBool $2 class MarkBoolсоздает EnumMap, инициализируемую по данной Map.
==Пример==
Анонимный участник

Навигация