ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 12.01.2024
Просмотров: 110
Скачиваний: 5
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
435
11.1. Применение аннотаций
Если автор аннотации объявил ее как повторяющуюся, то одну и ту же аннотацию можно повторить неоднократно следующим образом:
@BugReport(showStopper=true, reportedBy="Joe")
@BugReport(reportedBy={"Harry", "Carl"}) public void checkRandomInsertions()
11.1.3. Объявление аннотаций
До сих пор были представлены примеры аннотаций, применявшихся в объявлениях методов. Аннотации могут встречаться и во многих других ме- стах прикладного кода, которые можно разделить на две категории: объявле-
ния и места употребления типов. Аннотации могут появляться в объявлениях следующих элементов кода.
•
Классы (включая и перечисления) и интерфейсы (в том числе и интер- фейсы аннотаций).
•
Методы.
•
Конструкторы.
•
Переменные экземпляра (включая и константы перечислимого типа).
•
Локальные переменные (в том числе и те, что объявлены в цикле for и операторах try с ресурсами).
•
Переменные параметров и параметры оператора catch.
•
Параметры типа.
•
Пакеты.
В объявлениях классов и интерфейсов аннотации указываются перед клю- чевым словом class или interface следующим образом:
@Entity public class User { ... }
А в объявлениях переменных аннотации указываются перед типом пере- менной таким образом:
@SuppressWarnings("unchecked") List
Параметр типа в обобщенном классе или методе может быть аннотирован следующим образом:
public class Cache<@Immutable V> { ... }
Пакет аннотируется в отдельном файле package-info.java. Этот файл со- держит только операторы объявления и импорта пакета с предшествующими
Java_SE_9_for_the_Impatient_2nd_Edit.indb 435 16.04.2018 16:40:27
Глава 11
Аннотации
436
аннотациями, как показано ниже. Обратите внимание на то, что оператор import следует после оператора package, в котором объявляется пакет.
/**
Документирующий комментарий на уровне пакета
*/
@GPL(version="3") package com.horstmann.corejava; import org.gnu.GPL;
НА ЗАМЕТКУ. Аннотации локальных переменных и пакетов отбрасываются при компили- ровании класса. Следовательно, они могут быть обработаны только на уровне исходного кода.
11.1.4. Аннотации в местах употребления типов
Аннотация в объявлении предоставляет некоторые сведения об объявляе- мом элементе кода. Так, в следующем примере кода аннотацией утверждает- ся, что параметр userId объявляемого метода не является пустым:
public User getUser(@NonNull String userId)
НА ЗАМЕТКУ. Аннотация
@NonNull является частью каркаса Checker Framework
(
http://types.cs.washington.edu/checker-framework). С помощью этого кар- каса можно включать утверждения в прикладную программу, например, утверждение, что параметр не является пустым или относится к типу
String и содержит регулярное выражение. В таком случае инструментальное средство статистического анализа прове- рит достоверность утверждений в данном теле исходного кода.
А теперь допустим, что имеется параметр типа List
Именно здесь и пригодятся аннотации в местах употребления типов. Такую аннотацию достаточно указать перед аргументом типа следующим образом:
List<@NonNull String>
Подобные аннотации можно указывать в следующих местах употребления типов.
•
Вместе с аргументами обобщенного типа: List<@NonNull String>,
Comparator.<@NonNull String> reverseOrder().
•
В любом месте массива: @NonNull String[][] words (элемент массива words[i][j] не является пустым), String @NonNull [][] words (массив words не является пустым), String[] @NonNull [] words (элемент масси- ва words[i] не является пустым).
Java_SE_9_for_the_Impatient_2nd_Edit.indb 436 16.04.2018 16:40:27
437
11.1. Применение аннотаций
•
В суперклассах и реализуемых интерфейсах: class Warning extends
@Localized Message.
•
В вызовах конструкторов: new @Localized String(...).
•
Во вложенных типах: Map.@Localized Entry.
•
В приведении и проверках типов instanceof: (@Localized String) text, if (text instanceof @Localized String). (Аннотации служат для употребления только внешними инструментальными средствами. Они не оказывают никакого влияния на поведение приведения и проверки типов instanceof.)
•
В местах указания исключений: public String read() throws @Localized
IOException.
•
Вместе с метасимволами подстановки и ограничениями типов: List
<@Localized ? extends Message>, List Extends @Localized Message>.
•
В ссылках на методы и конструкторы: @Localized Message::getText.
Имеются все же некоторые места употребления типов, где аннотации не допускаются. Ниже приведены характерные тому примеры.
@NonNull String.class // ОШИБКА: литерал класса не
// подлежит аннотированию! import java.lang.@NonNull String; // ОШИБКА: импорт не
// подлежит аннотированию!
Аннотации можно размещать до или после других модификаторов досту- па вроде private и static. Обычно (хотя и не обязательно) аннотации в ме- стах употребления типов размещаются после других модификаторов досту- па, тогда как аннотации в объявлениях — перед другими модификаторами доступа. Ниже приведены характерные тому примеры.
private @NonNull String text;
// Аннотация в месте употребления типа
@Id private String userId;
// Аннотация в объявлении переменной
НА ЗАМЕТКУ. Как поясняется в разделе 11.2, автор аннотации должен указать место, в котором может появиться конкретная аннотация. Если аннотация допускается как в объявлении переменной, так и в месте употребления типа, а также применяется в объяв- лении переменной, то она указывается и в том и в другом месте. Рассмотрим в качестве примера следующее объявление метода:
1 2 3 4
public User getUser(@NonNull String userId)
Если аннотацию
@NonNull можно применять как в параметрах, так и в местах употре- бления типов, то параметр
userId аннотируется, а тип параметра обозначается как
@NonNull String.
Java_SE_9_for_the_Impatient_2nd_Edit.indb 437 16.04.2018 16:40:27
Глава 11
Аннотации
438
11.1.5. Явное указание получателей аннотаций
Допустим, что требуется аннотировать параметры, которые не изменяют- ся методом, как показано ниже.
public class Point { public boolean equals(@ReadOnly Object other) { ... }
}
В таком случае инструментальное средство, обрабатывающее данную ан- нотацию, после анализа следующего вызова:
p.equals(q)
посчитает, что параметр q не изменился. А как насчет ссылки p? При вызове данного метода переменная получателя this привязывается к ссылке p. Но ведь переменная получателя this вообще не объявляется, а следовательно, она и не может быть аннотирована.
На самом деле эту переменную можно объявить с помощью редко упо- требляемой разновидности синтаксиса, чтобы ввести аннотацию следующим образом:
public class Point { public boolean equals(@ReadOnly Point this,
@ReadOnly Object other) { ... }
}
Первый параметр в приведенном выше примере кода называется параме-
тром получателя. Он должен непременно называться this. Его тип относится к тому классу, объект которого создается.
НА ЗАМЕТКУ. Параметром получателя можно снабдить только методы, но не конструкто- ры. По существу, ссылка
this в конструкторе не является объектом данного типа до тех пор, пока конструктор не завершится. Напротив, аннотация, размещаемая в конструкто- ре, описывает создаваемый объект.
Конструктору внутреннего класса передается другой скрытый параметр, а именно: ссылка на объект объемлющего класса. Этот параметр также можно указать явным образом:
static class Sequence { private int from; private int to; class Iterator implements java.util.Iterator
Java_SE_9_for_the_Impatient_2nd_Edit.indb 438 16.04.2018 16:40:27
439
11.2. Определение аннотаций public Iterator(@ReadOnly Sequence Sequence.this) { this.current = Sequence.this.from;
}
}
}
Этот параметр именуется таким же образом, как и при ссылке на него:
ОбъемлющийКласс
.this. А его тип относится к объемлющему классу.
11.2. Определение аннотаций
Каждая аннотация должна быть объявлена в интерфейсе аннотаций с по- мощью синтаксиса @interface. Методы этого интерфейса соответствуют элементам аннотации. Например, аннотация Test модульного теста в JUnit определяется в следующем интерфейсе:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) public @interface Test { long timeout();
}
В объявлении @interface создается конкретный интерфейс Java. Инстру- ментальные средства, обрабатывающие аннотации, получают объекты клас- сов, реализующих интерфейс аннотаций. Когда, например, исполнитель текстов в инструментальном средстве JUnit получает объект класса, реализу- ющего интерфейс Test, он просто вызывает метод timeout(), чтобы извлечь элемент установки времени ожидания из конкретной аннотации Test.
Аннотации @Target и @Retention являются мета-аннотациями. Они слу- жат аннотациями к аннотации Test, обозначая места, где аннотация может произойти и где она доступна.
Значением мета-аннотации @Target служит массив объектов типа
ElementType, обозначающих элементы, к которым можно применить аннота- цию. В фигурных скобках можно указать любое количество типов элементов, как показано в следующем примере кода:
@Target({ElementType.TYPE, ElementType.METHOD}) public @interface BugReport
Все допустимые адресаты аннотаций перечислены в табл. 11.1. Компи- лятор проверяет, применяется ли аннотация только там, где это разреше- но. Так, если аннотация @BugReport применяется к переменной, то во время компиляции возникает ошибка.
Java_SE_9_for_the_Impatient_2nd_Edit.indb 439 16.04.2018 16:40:27
Глава 11
Аннотации
440
НА ЗАМЕТКУ. Аннотация без ограничения
@Target может употребляться в любых объ- явлениях, но не в параметрах и местах употребления типов. (Эти места были единствен- ными допустимыми адресами аннотаций в первой версии Java, где поддерживались ан- нотации.)
В мета-аннотации @Retention указывается место, где аннотация может быть доступна. Этих мест может быть только три, как поясняется ниже.
1. RetentionPolicy.SOURCE. Аннотация доступна для процессов исходно- го кода, но не включается в файлы классов.
2. RetentionPolicy.CLASS. Аннотация включается в файлы классов, но виртуальная машина не загружает их. Этот вариант выбирается по умолчанию.
3. RetentionPolicy.RUNTIME. Аннотация доступна во время выполнения и через прикладной программный интерфейс API для рефлексии.
Таблица 11.1. Типы элементов для аннотации @Target
Тип элемента
Где применяется аннотация
ANNOTATION_TYPE
Объявления типов аннотаций
PACKAGE
Пакеты
TYPE
Классы (включая и перечисления) и интерфейсы (в том числе и типов аннотаций)
METHOD
Методы
CONSTRUCTOR
Конструкторы
FIELD
Переменные экземпляра (включая и константы перечислимого типа)
PARAMETER
Параметры методов и конструкторов
LOCAL_VARIABLE
Локальные переменные
TYPE_PARAMETER
Параметры типов
TYPE_USE
Места употребления типов
Примеры выбора всех трех перечисленных выше мест для доступа к ан- нотациям приведены далее в этой главе. Имеются и другие мета-аннотации, они перечисляются полностью далее, в разделе 11.3.
Чтобы задать значение по умолчанию для элемента аннотации, достаточ- но указать оператор default после метода, определяющего этот элемент, как выделено ниже полужирным.
public @interface Test { long timeout() default 0L;
}
Java_SE_9_for_the_Impatient_2nd_Edit.indb 440 16.04.2018 16:40:27
441
11.3. Стандартные аннотации
В следующем примере кода демонстрируется, каким образом задается пу- стой массив и значение по умолчанию для аннотации:
public @interface BugReport {
String[] reportedBy() default {};
// Пустой массив по умолчанию
Reference ref() default @Reference(id=0);
// Значение по умолчанию для аннотации
}
ВНИМАНИЕ! Значения по умолчанию не хранятся вместе с аннотацией и вычисляются динамически. Если изменить значение по умолчанию и перекомпилировать аннотиро- ванный класс, во всех аннотированных его элементах будет использовано новое зна- чение по умолчанию, даже если файлы классов компилировались до изменения этого значения.
Интерфейсы аннотаций расширению не подлежат. Это означает, что для реализации интерфейсов нельзя предоставить конкретные классы. Вместо этого инструментальные средства обработки исходного кода и виртуальная машина генерируют классы и объекты-заместители по мере надобности.
11.3. Стандартные аннотации
В пакетах java.lang, java.lang.annotation и javax.annotation из при- кладного программного интерфейса Java API определяется целый ряд интер- фейсов аннотаций. Четыре из них относятся к мета-аннотациям, описываю- щим поведение интерфейсов аннотаций, а другие — к обычным аннотациям, служащим для аннотирования отдельных элементов в исходном коде. Все эти разновидности аннотаций перечислены в табл. 11.2, а подробнее они рассма- триваются в двух последующих разделах.
Таблица 11.2. Стандартные аннотации
Интерфейс аннотаций
Где применяется
Назначение
Override
Методы
Проверяет, переопределяет ли данный метод соответствующий метод из суперкласса
Deprecated
Все объявления
Помечает элемент кода как не рекомендован- ный к употреблению
SuppressWarnings
Все объявления, кроме пакетов
Подавляет предупреждения данного типа
SafeVarargs
Методы и конструкторы
Утверждает, что пользоваться аргументами пе- ременной длины безопасно
FunctionalInterface Интерфейсы
Помечает интерфейс как функциональный с единственным абстрактным методом
Java_SE_9_for_the_Impatient_2nd_Edit.indb 441 16.04.2018 16:40:27
Глава 11
Аннотации
442
Интерфейс аннотаций
Где применяется
Назначение
PostConstruct
Методы
Метод должен быть вызван сразу же после создания
PreDestroy или до удаления внедряемого объекта
Resource
Классы и интерфейсы, методы, поля
Класс и интерфейс помечаются как ресурс, ис- пользуемый повсеместно, а метод или поле — для внедрения зависимостей
Resources
Классы и интерфейсы
Обозначает массив ресурсов
Generated
Все объявления
Помечает элемент исходного кода как сформи- рованный инструментальным средством
Target
Аннотации
Обозначает места, где может быть применена данная аннотация
Retention
Аннотации
Обозначает места, где может быть применена данная аннотация
Documented
Аннотации
Обозначает, что данная аннотация должна быть включена в документацию на аннотиро- ванные элементы кода
Inherited
Аннотации
Обозначает, что данная аннотация наследуется подклассом
Repeatable
Аннотации
Обозначает, что данную аннотацию можно применить несколько раз к одному и тому же элементу кода
11.3.1. Аннотации для компиляции
Аннотация @Deprecated может быть присоединена к любым элемен- там кода, применение которых больше не поощряется. Компилятор выдаст предупреждение, если в исходном коде будет обнаружен элемент, не реко- мендованный к употреблению. Эта аннотация имеет то же назначение, что и дескриптор @deprecated документирующей документации. Тем не менее аннотация сохраняется вплоть до времени выполнения.
НА ЗАМЕТКУ. В комплект JDK входит утилита
jdeprscan, способная просмотреть не рекомендуемые к применению элементы в архивных JAR-файлах.
Аннотация @Override вынуждает компилятор проверять, что аннотируе- мый метод действительно переопределяет метод из суперкласса. Так, если объявляется следующий класс:
public class Point {
@Override public boolean equals(Point other) { ... }
}
Окончание табл. 11.2
Java_SE_9_for_the_Impatient_2nd_Edit.indb 442 16.04.2018 16:40:27