Главная страница
Навигация по странице:

  • Таблица 11.1.

  • PARAMETER Параметры методов и конструкторовLOCAL_VARIABLE Локальные переменныеTYPE_PARAMETER Параметры типовTYPE_USE

  • Таблица 11.2.

  • Deprecated Все объявленияПомечает элемент кода как не рекомендован- ный к употреблениюSuppressWarnings

  • SafeVarargs Методы и конструкторыУтверждает, что пользоваться аргументами пе- ременной длины безопасноFunctionalInterface

  • Resource Классы и интерфейсы, методы, поляКласс и интерфейс помечаются как ресурс, ис- пользуемый повсеместно, а метод или поле — для внедрения зависимостейResources

  • Generated Все объявленияПомечает элемент исходного кода как сформи- рованный инструментальным средствомTarget

  • Retention АннотацииОбозначает места, где может быть применена данная аннотацияDocumented

  • Inherited АннотацииОбозначает, что данная аннотация наследуется подклассомRepeatable

  • НА ЗАМЕТКУ.

  • @SuppressWarnings("unchecked")

  • Кей С. Хорстманн


    Скачать 1.62 Mb.
    НазваниеКей С. Хорстманн
    Дата07.04.2023
    Размер1.62 Mb.
    Формат файлаpdf
    Имя файлаjavase9bazovyykurs.pdf
    ТипДокументы
    #1044418
    страница3 из 4
    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 { private int current;
    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

    443
    11.3. Стандартные аннотации то компилятор известит об ошибке в связи с тем, что метод equals() не пе- реопределяет одноименный метод equals() из класса Object, поскольку па- раметр этого метода относится к типу Object, а не к типу Point.
    Аннотация @SuppressWarnings дает компилятору команду подавить пред- упреждения конкретного типа, как показано в следующем примере кода:
    @SuppressWarnings("unchecked") T[] result =
    (T[]) Array.newInstance(cl, n);
    Аннотация @SafeVarargs утверждает, что метод не нарушает свой пара- метр переменной длины (см. главу 6).
    Аннотация @Generated предназначена для применения в инструменталь- ных средствах генерирования кода. Любой генерируемый исходный код мо- жет быть аннотирован, чтобы отличать его от кода, написанного вручную.
    Например, в редакторе исходного текста можно скрыть генерируемый код, а в генераторе кода — удалить прежние версии генерируемого кода. Каждая аннотация должна содержать однозначный идентификатор генератора кода.
    Дополнительную строку с датой (в формате по стандарту ISO 8601) и строку комментариев указывать можно, но не обязательно:
    @Generated(value="com.horstmann.generator", date="2015-01-04T12:08:56.235-0700");
    В главе 3 был приведен пример употребления аннотации @Functional
    Interface. Она аннотирует преобразование адресатов лямбда-выражений, как показано ниже. Если в дальнейшем ввести в данный интерфейс еще один абстрактный метод, компилятор выдаст ошибку.
    @FunctionalInterface public interface IntFunction {
    R apply(int value);
    }
    Разумеется, такие аннотации должны быть введены в интерфейсы, описы- вающие отдельные функции. Имеются и другие интерфейсы с единственным абстрактным методом (например, интерфейс AutoCloseable), по существу, не являющиеся функциями.
    11.3.2. Аннотации для управления ресурсами
    Аннотации @PostConstruct и @PreDestroy применяются в средах, управ- ляющих сроком действия объектов, например, в веб-контейнерах и серверах приложений. Методы, помеченные этими аннотациями, должны вызываться сразу же после создания объекта или непосредственно перед его удалением.
    Аннотация @Resource предназначена для внедрения ресурсов. В каче- стве примера рассмотрим веб-приложение, осуществляющее доступ к базе
    Java_SE_9_for_the_Impatient_2nd_Edit.indb 443 16.04.2018 16:40:27

    Глава 11
    „
    Аннотации
    444
    данных. Безусловно, доступ к информации в базе данных не должен быть жестко закодирован в данном веб-приложении. Вместо этого у веб-контей- нера имеется свой пользовательский интерфейс для установки параметров подключения к базе данных, а также имя источника данных, определяемое в прикладном программном интерфейсе JNDI. Обращаться к этому источнику данных из веб-приложения можно следующим образом:
    @Resource(name="jdbc/employeedb") private DataSource source;
    Когда создается объект, содержащий приведенную выше переменную эк- земпляра, веб-контейнер внедряет ссылку на источник данных. Это означает, что он устанавливает в переменной экземпляра объект типа DataSource, на- страиваемый на имя "jdbc/employeedb".
    11.3.3. Мета-аннотации
    Мета-аннотации @Target и @Retention упоминались в разделе 11.2. А ме- та-документация @Documented предоставляет указание для инструментальных средств документирования вроде утилиты javadoc. Документируемые анно- тации следует рассматривать как разновидность модификаторов (например, private или static), употребляемых для документирования. Остальные ан- нотации не следует включать в документацию.
    Например, аннотация @SuppressWarnings не документируется. Если метод или поле содержит такую аннотацию, то особенности ее реализации мало- интересны читающему документацию на прикладной код. С другой стороны, аннотация @FunctionalInterface документируется, поскольку программи- сту важно знать, что аннотируемый ею интерфейс предназначен для описа- ния функции. Пример документируемой аннотации приведен на рис. 11.1.
    Мета-аннотация @Inherited применяется только к аннотациям классов.
    Если в классе имеется наследуемая аннотация, то все его подклассы автома- тически получают ту же самую аннотацию. Благодаря этому упрощается со- здание аннотаций, действующих аналогично маркерным интерфейсам (на- пример, интерфейсу Serializable).
    Допустим, аннотация @Persistent определяется с целью определить, что объекты класса могут быть сохранены в базе данных. В таком случае подклас- сы постоянных классов автоматически аннотируются как постоянные.
    @Inherited @interface Persistent { }
    @Persistent class Employee { . . . } class Manager extends Employee { . . . }
    // Этот класс также имеет аннотацию @Persistent
    Java_SE_9_for_the_Impatient_2nd_Edit.indb 444 16.04.2018 16:40:27

    445
    11.3. Стандартные аннотации
    Рис. 11.1. Документируемая аннотация
    Мета-аннотация @Repeatable позволяет применить одну и ту же аннота- цию неоднократно. Допустим, что аннотация @TestCase повторяется. В та- ком случае ею можно воспользоваться следующим образом:
    @TestCase(params="4", expected="24")
    @TestCase(params="0", expected="1") public static long factorial(int n) { ... }
    По ряду исторических причин разработчикам повторяющейся аннотации пришлось предоставить контейнерную аннотацию, содержащую повторяю- щиеся аннотации в массиве. Ниже показано, каким образом определяется аннотация @TestCase и ее контейнер.
    @Repeatable(TestCases.class)
    @interface TestCase {
    String params();
    String expected();
    }
    @interface TestCases {
    TestCase[] value();
    }
    Java_SE_9_for_the_Impatient_2nd_Edit.indb 445 16.04.2018 16:40:27

    Глава 11
    „
    Аннотации
    446
    Всякий раз, когда пользователь предоставляет две или больше аннота- ции @TestCase, они автоматически заключаются в оболочку аннотации
    @TestCases. Это усложняет обработку аннотации, как будет показано в сле- дующем разделе.
    11.4. Обработка аннотаций во время выполнения
    В приведенных до сих пор примерах было показано, каким образом анно- тации вводятся в исходные файлы и как определяются типы аннотаций. А те- перь настало время выяснить, какую же пользу можно извлечь из аннотаций.
    В этом разделе на простом примере поясняется обработка аннотаций во время выполнения с использованием прикладного программного интерфей- са API для рефлексии, рассматривавшегося в главе 4. Допустим, требуется сократить затраты труда на реализацию методов типа toString. Можно, конечно, написать обобщенный метод toString(), используя рефлексию, чтобы учесть имена и значения всех переменных экземпляра. Но допустим, что этот процесс требуется специально настроить, чтобы не включать в него все переменные экземпляра или пропустить имена классов и переменных.
    Например, для класса Point более предпочтительной может оказаться обо- значение координат точки [5,10] вместо обозначения Point[x=5,y=10]. Раз- умеется, в данный процесс можно внести и другие усовершенствования, но мы не будем этого делать ради простоты примера. Самое главное — проде- монстрировать в нем возможности процессора аннотаций.
    Все классы, в которых требуется извлечь выгоду из данного процесса, сле- дует снабдить аннотацией @ToString. Аннотировать следует и все перемен- ные экземпляра, которые должны быть включены в данный процесс. Аннота- ция @ToString определяется следующим образом:
    @Target({ElementType.FIELD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME) public @interface ToString { boolean includeName() default true;
    }
    Ниже приведены аннотированные классы Point и Rectangle. При этом преследуется цель представить прямоугольник в виде символьной строки с параметрами Rectangle[[5, 10], width=20,height=30].
    @ToString(includeName=false) public class Point {
    @ToString(includeName=false) private int x;
    @ToString(includeName=false) private int y;
    }
    Java_SE_9_for_the_Impatient_2nd_Edit.indb 446 16.04.2018 16:40:27

    1   2   3   4


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