Главная страница

Java. Полное руководство. 8-е издание. С. Н. Тригуб Перевод с английского и редакция


Скачать 25.04 Mb.
НазваниеС. Н. Тригуб Перевод с английского и редакция
АнкорJava. Полное руководство. 8-е издание.pdf
Дата28.02.2017
Размер25.04 Mb.
Формат файлаpdf
Имя файлаJava. Полное руководство. 8-е издание.pdf
ТипДокументы
#3236
страница21 из 90
1   ...   17   18   19   20   21   22   23   24   ...   90
ГЛАВА
Перечисления,
автоупаковка и аннотации (метаданные)
В настоящей главе рассматриваются три относительно новых дополнения к языку Java: перечисления, автоупаковка и аннотации (называемые также метаданными. Каждое из них увеличивает мощь языка, предлагая изящный подход к решению часто возникающих задач программирования. В главе также обсуждаются оболочки типов Java и рефлексия.
Версиям языка Java, предшествовавшим JDK 5, недоставало одного средства, необходимость в котором чувствовали многие программисты, — перечисления. В простейшей форме перечисление — это список именованных констант. Хотя Java включает и другие средства, имеющие похожую функциональность, такие как финальные переменные, многим программистам все жене хватало концептуальной чистоты перечислений — в особенности потому, что они применяются во многих других языках программирования. Начиная с JDK 5 перечисления были добавлены к языку Java и, наконец, стали доступны программистам.
В простейшей форме перечисления Java подобны перечислениям в других языках. Однако это сходство поверхностно. В языках вроде C++ перечисления просто представляют собой списки целочисленных констант. В языке Java перечисления определяют тип класса. За счет реализации перечислений в виде классов сама концепция перечисления значительно расширяется. Например, в языке Java перечисления могут иметь конструкторы, методы и переменные экземпляра. Таким образом, хотя воплощения перечислений пришлось ждать несколько лет, реализация их в языке Java стоила того.
Перечисления создаются с использованием ключевого слова enum. Например, ниже показано простое перечисление сортов яблок Перечисление сортов яблок
enum Apple {
Jonathan, GoldenDel,
RedDel, Winesap, Идентификаторы
Jonathan, GoldenDel итак далее называются константами перечисления Каждая из них явно объявлена как открытый статический финальный член класса
Apple. Более того, их тип — это тип перечисления, в котором они
Перечисления
Основные понятия о перечислениях
Ю Зак Часть I. Язык объявлены в данном случае это
Apple. То есть в языке Java эти константы называются самотипизированными, причем само относится к окружающему перечислению.
Объявив перечисление, вы можете создавать переменные этого типа. Однако даже несмотря на то, что перечисления определяют тип класса, вы не можете создавать объекты этого типа с помощью оператора new. Вместо этого вы объявляете и используете переменную перечисления почти таким же образом, как это делается с элементарными типами. Например, ниже объявляется переменная ар перечислимого типа
Apple.
Apple ар;
Поскольку переменная ар имеет тип
Apple, присвоить ей можно только те значения, которые определены в перечислении. Например, здесь переменной ар присваивается значение ар = A p p l e Обратите внимание на то, что значению
RedDel предшествует тип Две перечислимые константы можно проверять на равенство с помощью оператора отношения ==. Например, следующий оператор сравнивает переменную ар с константой
Apple.
GoldenDel.
if (ар == Apple.GoldenDel) // Перечислимые значения также могут быть использованы в управляющей конструкции switch. Конечно же, все операторы case должны использовать константы из того же перечисления enum, что и выражение switch. Например, следующий оператор switch абсолютно корректен Использование enum для управления switch, ар) {

case Jonathan:
/ / . .
.
case Winesap:
/ / . . Обратите внимание на то, что в операторах case имена перечислимых констант используются без квалифицированного имени их типа перечисления. То есть применяется
Winesap, а не
Apple.
Winesap. Дело в том, что тип перечисления в операторе switch уже неявно задает тип перечисления для операторов case. Нет необходимости квалифицировать константы в операторах case именем типа их перечисления. Фактически попытка сделать это приведет к ошибке компиляции.
Когда константа перечисления отображается, скажем, методом println(), выводится ее имя. Например, следующая строка кода
System.out.println(Apple.Winesapp)
отобразит имя Приведенная ниже программа собирает все вместе и демонстрирует применение перечисления
Apple.
// Перечисление сортов яблок
enum Apple {
Jonathan, GoldenDel, RedDel, Winesap, Cortland
}
class EnumDemo {
public static void main(String a r g s [])
Глава 12. Перечисления, автоупаковка и аннотации (метаданные)
2 9 1
Apple ар;
ар = A p p l e .RedDel;
// Вывод значения Значение ар " + ар r ар = A p p l e .GoldenDel;
// Сравнение двух перечислимых значений f (ар == A p p l e ар содержит GoldenDel.\n");
// Применение enum для управления оператором ар) {
case Jonathan:
System.out.println("Jonathan красный
break;
case GoldenDel:
System.out.println("Golden Delicious желтый
break;
case RedDel:
System.out.println("Red Delicious красный
break;
case Winesap:
System.out.println("Winesap красный
break;
case Cortland:
System.out.println("Cortland красный Эта программа создает следующий вывод.
Значение ар ар содержит GoldenDel.
Golden Delicious желтый.
Методы
values
( ) и
valueOf
( Перечисления автоматически включают два предопределенных метода val­
ues (
) и valueOf ()
. Их общая форма выглядит так static

тип_перечисления
[] v a l u e s ()
public static
тип_перечисления
valueOf(String
строка)
Метод values
() возвращает массив, содержащий список констант перечисления. Метод valueOf
() возвращает константу перечисления, значение которой соответствует строке, переданной в аргументе строка. В обоих случаях тип перечисления это тип перечисления. Например, в случае с перечислением
Apple, показанным выше, типом возвращаемого значения
Apple. valueOf
(
"Winesapp"
) будет
Winesарр.
В следующей программе демонстрируется применение методов values
() и valueOf().
// Использование встроенных методов перечислений Перечисление сортов яблок
enum Apple {

2 9 Часть I. Язык Java
Jonathan, GoldenDel, RedDel, Winesap, Cortland
}
class EnumDemo2 {
public static void main(String a r g s [])
{
Apple Константы Apple:");
// применение v a l u e s ()
Apple allapples[] = A p p l e .v a l u e s ();
for(Apple a :
allapples)
System.out.println(a);
System.out.p r intln();
// применение v a l ueOf()
ap = ар содержит " + ap) Вывод этой программы таков.
Константы Apple:
Jonathan
GoldenDel
RedDel
Winesap
Cortland
ap содержит Обратите внимание на то, что программа использует стиль “for-each” цикла f o r для перебора массива констант, возвращенных методом v a l u e s (). В целях демонстрации создается переменная a l l a p p l e s , которой присваивается ссылка на массив перечислимых значений. Но это необязательно, поскольку цикл f o r можно написать, как показано ниже, избежав применения переменной a l l a p p l e s .
for(Apple a :
Ap p l e .v a l u e s (Обратите также внимание на то, как значение, соответствующее имени
Wines app, получается вызовом метода v a l u e O f (ар = Как объяснялось ранее, метод v a l u e O f () возвращает перечислимое значение, ассоциированное с именем константы, переданным в строке.
На заметку Программисты на C/C++ обратят внимание на то, что в языке Java значительно упрощено преобразование между читабельной для человека формой константы перечисления и ее бинарным значением. Это существенное преимущество подхода к перечислениям языка Перечисления в Java являются типами классов
Как уже объяснялось, перечисление в Java — это тип класса. Хотя вы не можете создать экземпляр перечисления с помощью оператора new, в остальном перечисление обладает всеми возможностями, которые имеются у других классов
Глава 12. Перечисления, автоупаковка и аннотации (метаданные)
2 9 Тот факт, что перечисление определяет класс, придает такую мощь перечислениям Java, которой лишены перечисления в других языках. Например, вы можете предоставлять им конструкторы, добавлять переменные экземпляров и методы и даже реализовывать интерфейсы.
Важно понимать, что каждая константа перечисления является объектом своего класса перечисления. То есть, когда вы определяете конструктор для перечисления, он вызывается при каждом создании константы перечисления. Также каждая константа перечисления имеет свою собственную копию переменных экземпляра, объявленных перечислением. Например, рассмотрим следующую версию перечисления A p ple.
// Использование конструктора enum, переменной экземпляра и метода
enum Apple {
Jonathan(10), GoldenDel(9), RedD e l (12), W i n esap(15), Cortland(8);
private int price; // цена каждого яблока Конструктор p) { price = p; }
int getPrice() { return price; }
}
class EnumDemo3 {
public static void main(String a r g s [])
{
Apple ap;
// Отобразить цену Winesap.
System.out.println("Winesap стоит " +
Apple.Winesap.getPrice() +
" центов Отобразить цены всех сортов яблок r i Все цены яблока" стоит " + a.getPrice() +
" центов.");
}
}
Ниже показан вывод программы стоит 15 центов.

Все цены яблок стоит 10 центов стоит 9 центов стоит 12 центов стоит 15 центов стоит 8 центов.
Данная версия перечисления A p p le добавляет следующее. Первое — это переменная экземпляра p r i c e , которая применяется для хранения цены каждого сорта яблок. Второе — конструктор A p p le ( ), которому передается цена яблок. Третье — метод g e t P r i c e (), возвращающий значение цены.
Когда в методе mai n () объявляется переменная ар, конструктор A p p le () вызывается однажды для каждой объявленной константы. Следует отметить, что аргументы конструктору передаются помещением их в скобки после каждой константы, GoldenDel(9), Red D e l (12), W i nesap(15), Cortland(8);


2 9 Часть I. Язык Эти переменные передаются параметру р конструктора
Apple ()
, который затем присваивает их переменной экземпляра price. Опять же конструктор вызывается однажды для каждой константы.
Поскольку каждая константа перечисления имеет собственную копию переменной экземпляра price, вы можете получить цену определенного сорта яблок вызовом метода get
Price
(). Например, цену сорта
Winesap в методе main
() получим следующим вызовом.
Apple.Winesap.getPrice()
Цены всех сортов получим при переборе перечисления в цикле for. Поскольку копия переменной экземпляра price существует для каждой перечислимой константы, значение, ассоциированное с одной константой, отделено и отличается от значения, ассоциированного с другой константой. Это мощная концепция, которая доступна только в случае реализации перечислений в виде классов, как это сделано в языке Хотя предыдущий пример содержит только один конструктор, перечисление может представлять две или более перегруженных форм, как это может делать любой другой класс. Например, приведенная ниже версия перечисления
Apple предлагает конструктор по умолчанию, инициализирующий цену значением
-1, означающим, что цена не указана Использование конструкторов enum.

enum Apple {
Jonathan(10), GoldenDel(9), RedDel, Win e s a p (15), Cortland(8);
private int price; // цена каждого яблока Конструктор p) { price = p; }
// Перегруженный конструктор p p l e () { price = -1; }
int getPrice() { return price; Обратите внимание на то, что в этой версии константе
RedDel не передается аргумент. Это означает, что вызывается конструктор по умолчанию и переменная цены
RedDel устанавливается равной Здесь есть два ограничения относительно перечислений. Во-первых, перечисление не может наследоваться от другого класса. Во-вторых, перечисление не может быть суперклассом. Это значит, что перечисление не может быть расширено. Во всем остальном перечисление ведет себя, как любой другой тип класса. Ключевой момент — помнить, что каждая константа перечисления является объектом класса, в котором она определена.
Перечисления наследуются от класса
E n u Хотя вы не можете наследовать суперкласс при объявлении перечисления, все перечисления автоматически наследуют класс j ava.
1 ang.
Enum. Этот класс определяет несколько методов, доступных к использованию всеми перечислениями. Класс
Enum подробно рассматривается в части II, но три его метода требуют описания прямо сейчас
Глава 12. Перечисления, автоупаковка и аннотации (метаданные)
2 9 Вы можете получить значение, которое указывает позицию константы в списке констант перечисления. Это называется порядковым значением (ordinal value), которое извлекается с помощью вызова метода ordinal ()
, показанного ниже int Этот метод возвращает порядковое значение вызывающей константы. Порядковые значения начинаются с нуля. То есть в перечислении
Apple константа
Johnatan имеет порядковое значение 0, константа
GoldenDel
— 1, константа
— 2 и т.д.
Вы можете сравнить порядковые значения двух констант одного итого же перечисления с помощью метода compareTo ()
. Он имеет следующую общую форму int compareTo(

тип_перечисления е)
Здесь тип перечисления тип перечисления, а е
— константа, которую нужно сравнить с вызывающей константой. Помните, что вызывающая константа и е должны относиться к одному перечислению. Если вызывающая константа имеет порядковое значение меньше чем е, то метод compareTo
() возвращает отрицательное значение. Если два порядковых значения одинаковы, возвращается нуль. Если вызывающая константа имеет порядковое значение больше чем е, то возвращается положительное значение.
Вы можете сравнить на эквивалентность перечислимую константу с любым другим объектом, используя метод equals
() — переопределенный метод equals
() класса
Object. Хотя метод equals
() может сравнивать перечислимые константы с любым другим объектом, эти два объекта будут эквивалентны только в случае, если оба являются ссылкой на одну и туже константу из одного итого же перечисления. Простое совпадение порядковых значений не заставит метод equals
() вернуть значение true, если две константы принадлежат разным перечислениям.
Помните, что вы можете сравнивать две ссылки перечислений на эквивалентность, используя оператор В следующей программе демонстрируется применение методов ordinal
( )
,
compareTo
() и equals().
// Демонстрация ordinal О ,
compareTo() и equals().
// Перечисление сортов яблок
enum Apple {
Jonathan, GoldenDel, RedDel, Winesap, Cortland
}
class EnumDemo4 {
public static void main(String args[])
{
Apple ар, a p 2 ,
a p 3 ;
// Получить все порядковые значения с помощью Вот все константы " +
" и их порядковые значения ");
for(Apple а :
A p p l e .v a l u e s (а + " " + a .ordinal());
ap = A p p l e .RedDel;
ap2 = A p p l e .GoldenDel;
ap3 = A p p l e .RedDel;
System.out.println();
// Демонстрация compareTo() и equals()

2 9 Часть I. Язык Java
if(ap.compareTo(ap2)
< 0)
System.out.println(ap + " идет перед " + a p 2 )
;
if(ap.compareTo(ap2)
> 0)
System.out.println(ap2 + " идет перед " + ap) ;
if(ap.compareTo(арЗ)
== 0)
System.out.println(ap + " эквивалентно " + арЗ);
System.out.println();
if(ap.equals(ap2))
System.out.println("Error!
") ;
if(ap.equals(арЗ))
System.out.println(ap + " equals " + арЗ);
if(ap == арЗ)
System.out.println(ap + " == " + ар З Ниже показан вывод этой программы.
Вот все константы и их порядковые значения 0
GoldenDel 1
RedDel 2
Winesap 3
Cortland 4
GoldenDel идет перед RedDel
RedDel эквивалентно RedDel
RedDel эквивалентно RedDel
RedDel =
= Еще один пример перечисления
Прежде чем двигаться дальше, рассмотрим еще один пример применения перечисления. В главе 9 создавалась программа для автоматического принятия решений. В этой версии переменные
NO, YES, MAYBE, LATER, SOON и
NEVER были объявлены в интерфейсе и использованы для представления возможных ответов. Хотя в таком подходе нет ничего технологически неверного, применение перечислений — более подходящее решение. Здесь представлена усовершенствованная версия этой программы, которая использует перечисление по имени
Answers для представления ответов. Можете сравнить эту версию с оригинальной из главы 9.
// Усовершенствованная версия программы принятия решений
/ / из главы 9. В этой версии для представления
// используется enum, а не переменные экземпляра java.util.Random;
// Перечисление возможных ответов
enum Answers {
NO, YES, MAYBE, LATER, SOON, NEVER
}
Глава 12. Перечисления, автоупаковка и аннотации (метаданные)
2 9 7
class Question {
Random rand = new R a ndom();
Answers ask() {
int prob = (int) (100 * rand.nextDouble()
if (prob < 15)
return Answers.MAYBE; // 15%
else if (prob < 30)
return Answers.NO;
// 15%
else if (prob < 60)
return Answers.YES; // 3 0%
else if (prob < 75)
return Answers.LATER; // 15%
else if (prob < 98)
return Answers.SOON;
// 13%
else
return Answers.NEVER; // 2%
}
}
class AskMe {
static void answer(Answers result) {
1   ...   17   18   19   20   21   22   23   24   ...   90


написать администратору сайта