IT-Storm

У программного обеспечения и церквей много общего — мы сначала строим их, а потом на них молимся

Menu

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

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

С точки зрения программирования перечисления оказываются удобными в тех случаях, когда требуется определить набор значений, представляющих коллекцию элементов. Разумеется, для этих целей вполне можно использовать константы типа final, но перечисления обеспечивают более структурированный подход к решению подобных задач.

Основные сведения о перечислениях
Перечисления создаются с использованием ключевого слова enum.
// Перечисление, представляющее разновидности транспортных средств
enum Transport {
    CAR, TRUCK, AIRPLANE, TRAIN, ВОАТ
} 
Идентификаторы CAR, TRUCK и т.д. - константы перечисления.

Каждый из них неявно объявлен как открытый (public), статический (static) член перечисления Transport. Типом этих констант является тип перечисления (в данном случае Transport). В Java подобные константы называют самотипизированными, где приставка "само" относится к перечислению, которому они принадлежат.


Определив перечисление, можно создавать переменные этого типа. Однако, несмотря на то что перечисление - это тип класса, объекты этого класса создаются без привлечения оператора new .
Например, для определения переменной tp типа Transport понадобится следующая строка кода:
Transport tp;
Поскольку переменная tp относится к типу Transport, ей можно присваивать только те значения, которые определены для данного типа. Например, в следующей строке кода переменной tp присваивается значение AIRPLANE:
tp = Transport.AIRPLANE;
Обратите внимание на то, что значению AIRPLANE предшествует указанный через точку тип Transport.

Для проверки равенства констант перечислимого типа используется операция сравнения (==). Например, в следующей строке кода содержимое переменной tp сравнивается с константой TRAIN.
if (tp == Transport.TRAIN)
Перечисления можно использовать в качестве селектора в переключателе switch
// Использование перечисления для управления оператором switch
switch (tp) {
	case CAR:
	//...
	case TRUCK:
	//...
}
Заметьте, что в ветвях case оператора switch используются простые имена констант, а не уточненные. Так, в приведенном выше коде вместо полного имени Transport.TRUCK используется простое имя TRUCК. Этого достаточно, поскольку тип перечисления в выражении оператора swi tch неявно задает тип констант в ветвях case. Более того, если вы попытаетесь указать тип констант явным образом, компилятор выведет сообщение об ошибке. При отображении константы перечислимого типа, например, с помощью метода println(), выводится ее имя. Так, в результате выполнения следующего оператора отобразится имя ВОАТ:
System.out.println(Transport.BOAT);
Прежде чем продвигаться дальше, следует сделать одно замечание. Имена констант в перечислении Transport указываются прописными буквами (например, одна из констант перечисления называется CAR, а не car). Однако это требование не является обязательным.
 
Перечисления Java являются типами классов
В Java, в отличие от других языков программирования, перечисления реализованы как типы классов. И хотя для создания экземпляров класса enum не требуется использовать оператор new, во всех остальных отношениях они ничем не отличаются от классов. Реализация перечислений Java в виде классов позволила значительно расширить их возможности. В частности, допускается определение конструкторов перечислений, добавление в них объектных переменных и методов и даже создание перечислений, реализующих интерфейсы.

Методы values() и valueOf()
Все перечисления автоматически включают два предопределенных метода: values() и valueOf(), общие формы объявления которых приведены ниже.

public static перечислимый_тип[] values()
public static перечислимый_тип valueOf(String str)

Метод values() возвращает массив, содержащий список констант перечисления, а метод valueOf() возвращает значение перечислимого типа, ассоциированное с именем константы, представленной в виде строки. В обоих случаях перечислимый_тип - это тип перечисления. Например, в случае рассмотренного выше перечисления Transport вызов метода Transport.valueOf("TRAIN") возвратит значение TRAIN типа Transport.

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

Очень важно, чтобы вы понимали, что в перечислении каждая константа является объектом класса данного перечисления.

Таким образом, перечисление может иметь конструкторы, методы и переменные экземпляра. Если определить для объекта перечислимого типа конструктор, он будет вызываться всякий раз при создании константы перечисления. Для каждой константы перечислимого типа можно вызвать любой метод, определенный в перечислении. Кроме того, у каждой константы перечислимого типа имеется собственная копия любой переменной экземпляра, определенной в перечислении.
// Использование конструктора, переменной экземпляра и
// метода перечисления
enum Transport {
	CAR(65), TRUCK(55), AIRPLANE(6OO), TRAIN(70), ВОАТ(22);
	private int speed; // типичная скорость транспортного

	// конструктор
	Transport(int s) { 
		speed s; 
	}
	// метод
	int getSpeed() { 
		return speed; 
	}
}
// Например: отобразить скорость самолета
System.out.println("Tипичнaя скорость самолета: " + Transport.AIRPLANE.getSpeed() + 
			" миль в час\n");
В этой версии программы перечисление Transport претерпело ряд изменений.
Во-первых, появилась переменная экземпляра speed, используемая для хранения скорости движения транспортного средства.
Во-вторых, в перечисление Transport добавлен конструктор, которому передается значение скорости.
И в-третьих, в перечисление добавлен метод getSpeed(), возвращающий значение переменной speed, т.е. скорость движения данного транспортного средства.

Когда переменная tp объявляется (например в методе main()):
Transport tp;
для каждой константы перечисления автоматически вызывается конструктор Transport(). Аргументы, передаваемые конструктору, указываются в скобках после имени константы, как показано ниже:
CAR(65), TRUCK(55), AIRPLANE(600), TRAIN(70), ВОАТ(22);
Числовые значения, передаваемые конструктору Transport () через параметр s, присваиваются переменной speed. Обратите внимание на то, что список констант перечислимого типа завершается точкой с запятой. Последней в этом списке указана константа ВОАТ. Точка с запятой требуется в том случае, если класс перечисления содержит наряду с константами и другие члены.

У каждой константы перечислимого типа имеется собственная копия переменной speed, что позволяет получить скорость передвижения конкретного транспортного средства, вызвав метод getSpeed(). Например, в методе main() скорость самолета определяется с помощью следующего вызова:
Traпsport.AIRPLANE.getSpeed()
Такой принцип организации перечислений довольно эффективен, но он возможен только в том случае, если перечисления реализованы в виде классов, как это сделано в Java.
В предыдущем примере использовался только один конструктор, но перечисления, как и обычные классы, допускают любое число конструкторов.
 
Два важных ограничения
В отношении перечислений действуют два ограничения. Во-первых, перечисление не может быть подклассом другого класса. И во-вторых, перечисление не может выступать в роли суперкласса. Иными словами, перечислимый тип enum нельзя расширять. Если бы это было не так, перечисления вели бы себя как обычные классы.

Основной же особенностью перечислений является создание констант в виде объектов того класса, в котором они определены.


ВОПРОС. Теперь, когда в Java включены перечисления, можно ли считать, что финальные (final) переменные больше не нужны?

ОТВЕТ. Нет, нельзя. Перечисления удобны в тех случаях, когда приходится иметь дело со списками элементов, которые должны представляться идентификаторами. В то же время финальные переменные целесообразно применять для хранения постоянных значений, например размеров массивов, которые многократно используются в программе. Таким образом, у каЖдого из этих языковых средств имеется своя область применения. Преимущество перечислений проявляется в тех случаях, когда переменные типа final не совсем удобны.

Перечисления наследуются от класса Enum
Несмотря на то что перечисление не может наследовать суперкласс, все перечисления автоматически наследуют переменные и методы класса java.lang.Enum. В этом классе определен ряд методов, доступных всем перечислениям. И хотя большинство этих методов используются редко, тем не менее два из них иногда применяются в программах на Java. Это методы: ordinal() и compareTo().
Метод ordinal() позволяет получить так называемое порядковое значение, которое указывает позицию константы в списке констант перечисления. Ниже приведена общая форма объявления метода ordinal():

final int ordinal()

Этот метод возвращает порядковое значение вызывающей константы. Отсчет порядковых значений начинается с нуля.
Для сравнения порядковых значений двух констант одного и того же перечисления можно воспользоваться методом compareTo(). Ниже приведена общая форма объявления этого метода.

final int соmраrеТо(перечислимый_тип е)

Здесь перечислимый_ тип - это тип перечисления, а е - константа, сравниваемая с вызывающей константой. При этом не следует забывать, что вызывающая константа и константа е должны относиться к одному и тому же перечислимому типу. Если порядковое значение вызывающей константы меньше порядкового значения константы е, то метод compareTo() возвращает отрицательное значение. Если же их порядковые значения совпадают, возвращается нулевое значение. И наконец, если порядковое значение вызывающей константы больше порядкового значения константы е, метод возвращает положительное значение.

Пример использования Enum:
public enum Color {
	WHITE("белый"), GREEN("зелёный"), BLACK("чёрный"), BLUE("синий");
	private String title; //строка из конструктора

	Color(String str) {
		this.title = str;
//		title = str + "!!!"; //так тоже работает
	}
	public String getTitle() {
		return this.title;
//		return title; //так тоже работает
	}
	@Override
	public String toString() {
		return "цвет: " + this.title;
	}
}
В завершение:
Константы в Enum это не тип String. Чтобы все встало на свои места, обратите внимание на одно - все константы в Enum это ОБЪЕКТЫ. Ведь enum это по сути класс. Следовательно при присваивании в коде переменной х (типа Enum), константы, из соответствующего описанного Enumа - в этот момент создаётся объект этого Enuma, а в конструктор (если он не дефолтный) попадает значение указанное в скобках у выбранной константы (В ОПИСАНИИ ЭТОГО Enumа). Следовательно можно изменить поведение полученного объекта, например в какое-то поле этого Enumа занести принятое конструктором значение и использовать его (поле) в переопределённом методе toString() например.

Enum - перечисление, используется для создания неизменного набора, который будет использоваться в логике программы. Решили написать карточную игру в дурака? - можете создать перечеслинение из 4-ёх мастей. и 9-ти карт по старшинству. Решили создать крестики нолики - удобно будет использовать перечисление из двух элементов. Если кто то сталкивался с 1С, то там так же есть перечисления, это например хозяйственные операции документов, в зависимости от которых программа будет вести себя по разному. Например вариант резервирования товара при заказе клиента, товар можно зарезервировать "из наличия", "из заказа поставщику", "под будущий заказ". Примеров из жизни можно привести очень много даже в Яндекс картах маршрут можно построить 3 способами "на общественном транспорте", "на авто", "пешком". В зависимости от того что выбрал пользователь маршрут будет строиться по-разному, но пока он будет построен программа 1000 раз проверит какой тип маршрута запросил пользователь. Пользуясь перечислениями в коде, проверки делать проще как минимум в условных операторах, без ошибок и опечаток.

Надеюсь что эта статья и примеры помогут с пониманием, как и когда можно применять перечисления. ;)

Java