В языке программирования Java аннотация — это форма синтаксических метаданных , которые можно добавлять в исходный код Java . [1] Классы , методы , переменные , параметры и пакеты Java могут быть аннотированы. Подобно тегам Javadoc , аннотации Java можно читать из исходных файлов. В отличие от тегов Javadoc , аннотации Java также можно встраивать в файлы классов Java, созданные компилятором Java, и читать из них. Это позволяет виртуальной машине Java сохранять аннотации во время выполнения и читать их через отражение . [2] Можно создавать метааннотации из существующих в Java. [3]
Платформа Java имеет различные механизмы аннотаций ad-hoc , например, transient
модификатор или @Deprecated
тег javadoc. Запрос спецификации Java JSR-175 представил средство аннотации общего назначения (также известное как метаданные ) в Java Community Process в 2002 году; оно получило одобрение в сентябре 2004 года. [4]
Аннотации стали доступны в самом языке, начиная с версии 1.5 Java Development Kit (JDK). apt
Инструмент предоставлял временный интерфейс для обработки аннотаций во время компиляции в JDK версии 1.5; JSR-269 формализовал это, и он был интегрирован в компилятор javac в версии 1.6.
Java определяет набор аннотаций, встроенных в язык. Из семи стандартных аннотаций три являются частью java.lang , а остальные четыре импортируются из java.lang.annotation. [5] [6]
Аннотации, применяемые к коду Java:
@Override
— Проверяет, что метод является переопределением . Вызывает ошибку компиляции , если метод не найден ни в одном из родительских классов или реализованных интерфейсов .@Deprecated
— Помечает метод как устаревший. Вызывает предупреждение компиляции, если метод используется.@SuppressWarnings
— Дает указание компилятору подавлять предупреждения времени компиляции, указанные в параметрах аннотации.Аннотации, применяемые к другим аннотациям (также известные как «метааннотации»):
@Retention
— Указывает, как хранится отмеченная аннотация: только в коде, скомпилирована в класс или доступна во время выполнения через отражение.@Documented
— Отмечает еще одну аннотацию для включения в документацию.@Target
— Отмечает другую аннотацию, чтобы ограничить тип элементов Java, к которым может применяться аннотация.@Inherited
— Помечает другую аннотацию для наследования подклассами аннотированного класса (по умолчанию аннотации не наследуются подклассами).Начиная с Java 7 в язык были добавлены три дополнительные аннотации.
@SafeVarargs
— Подавляет предупреждения для всех вызывающих методов или конструкторов с параметром generics varargs , начиная с Java 7.@FunctionalInterface
— Указывает, что объявление типа должно быть функциональным интерфейсом , начиная с Java 8.@Repeatable
— Указывает, что аннотация может быть применена более одного раза к одному и тому же объявлению, начиная с Java 8.Этот пример демонстрирует использование аннотации @Override
. Он предписывает компилятору проверять родительские классы на предмет соответствия методов. В этом случае генерируется ошибка, поскольку gettype()
метод класса Cat на самом деле не переопределяет getType()
класс Animal, как это требуется, из-за несоответствия case . Если бы аннотация отсутствовала, в классе Cat был бы создан @Override
новый метод name .gettype()
публичный класс Животное { публичный void speak () { } public String getType () { return "Общее животное" ; } } public class Cat extends Animal { @Override public void speak () { // Это хорошее переопределение. System . out . println ( "Мяу." ); } @Override public String gettype () { // Ошибка компиляции из-за опечатки: должно быть getType(), а не gettype(). return "Cat" ; } }
Объявления типа аннотации похожи на обычные объявления интерфейса. Знак @ предшествует ключевому слову "interface".
// @Twizzle — это аннотация к методу toggle(). @Twizzle public void toggle () { } // Объявляет аннотацию Twizzle. public @interface Twizzle { }
Аннотации могут включать набор пар ключ-значение, которые моделируются как методы типа аннотации. Каждое объявление метода определяет элемент типа аннотации. Объявления методов не должны иметь никаких параметров или предложения throws. Типы возвращаемых значений ограничены примитивами , строками , классами, перечислениями , аннотациями и массивами предыдущих типов. Методы могут иметь значения по умолчанию .
// То же, что: @Edible(value = true) @Edible ( true ) Элемент item = new Carrot (); public @interface Edible { логическое значение () по умолчанию false ; } @Author ( first = "Oompah" , last = "Loompah" ) Книга книга = новая Книга (); public @interface Author { Первая строка (); Последняя строка (); }
Сами аннотации могут быть аннотированы для указания того, где и когда их можно использовать:
@Retention ( RetentionPolicy . RUNTIME ) // Сделать эту аннотацию доступной во время выполнения через отражение. @Target ({ ElementType . METHOD }) // Эту аннотацию можно применять только к методам класса. public @interface Tweezable { }
Компилятор резервирует набор специальных аннотаций (включая @Deprecated
, @Override
и @SuppressWarnings
) для синтаксических целей.
Аннотации часто используются фреймворками как способ удобного применения поведения к определяемым пользователем классам и методам, которые в противном случае должны быть объявлены во внешнем источнике (например, в файле конфигурации XML) или программно (с вызовами API). Ниже, например, представлен аннотированный класс данных JPA :
@Entity // Объявляет это компонентом сущности @Table ( name = "people" ) // Сопоставляет компонент с таблицей SQL "people" public class Person implements Serializable { @Id // Сопоставляет это со столбцом первичного ключа. @GeneratedValue ( strategy = GenerationType . AUTO ) // База данных сгенерирует новые первичные ключи, а не мы. private Integer id ; @Column ( length = 32 ) // Усечение значений столбцов до 32 символов. private String name ; public Integer getId () { return id ; } public void setId ( Integer id ) { this.id = id ; } public String getName () { return name ; } public void setName ( имя строки ) { this.name = имя ; } }
Аннотации не являются вызовами методов и сами по себе ничего не делают. Вместо этого объект класса передается реализации JPA во время выполнения , которая затем извлекает аннотации для генерации объектно-реляционного отображения .
Полный пример приведен ниже:
пакет com.annotation ; import java.lang.annotation.Documented ; import java.lang.annotation.ElementType ; import java.lang.annotation.Inherited ; import java.lang.annotation.Retention ; import java.lang.annotation.RetentionPolicy ; import java.lang.annotation.Target ; @Documented @Retention ( RetentionPolicy.RUNTIME ) @Target ( { ElementType.TYPE , ElementType.METHOD , ElementType.CONSTRUCTOR , ElementType.ANNOTATION_TYPE , ElementType.PACKAGE , ElementType.FIELD , ElementType.LOCAL_VARIABLE } ) @Inherited public @interface Незавершенный { public enum Приоритет { НИЗКИЙ , СРЕДНИЙ , ВЫСОКИЙ } String value (); String [] changedBy () по умолчанию "" ; String [] lastChangedBy () по умолчанию "" ; Приоритет priority () по умолчанию Приоритет . СРЕДНИЙ ; String createdBy () по умолчанию "Джеймс Гослинг" ; String lastChanged () по умолчанию "2011-07-08" ; }
пакет com.annotation ; public @interface UnderConstruction { String owner () по умолчанию "Патрик Нотон" ; String value () по умолчанию "Объект находится в стадии строительства." ; String createdBy () по умолчанию "Майк Шеридан" ; String lastChanged () по умолчанию "2011-07-08" ; }
пакет com.validators ; импорт javax.faces.application.FacesMessage ; импорт javax.faces.component.UIComponent ; импорт javax.faces.context.FacesContext ; импорт javax.faces.validator.Validator ; импорт javax.faces.validator.ValidatorException ; import com.annotation.UnderConstruction ; import com.annotation.Unfinished ; import com.annotation.Unfinished.Priority ; import com.util.Util ; @UnderConstruction ( owner = "Jon Doe" ) public class DateValidator implements Validator { public void validate ( контекст FacesContext , компонент UIComponent , значение Object ) throws ValidatorException { String date = ( String ) value ; String errorLabel = "Введите допустимую дату." ; if ( ! component.getAttributes ( ). isEmpty ()) { errorLabel = ( String ) component.getAttributes ( ) . get ( " errordisplayval " ); } если ( ! Util . validateAGivenDate ( date )) { @Unfinished ( changedBy = "Steve" , value = "добавлять сообщение в контекст или нет, подтвердить" , priority = Priority . HIGH ) FacesMessage message = new FacesMessage (); message . setSeverity ( FacesMessage . SEVERITY_ERROR ); message . setSummary ( errorLabel ); message . setDetail ( errorLabel ); throw new ValidatorException ( message ); } } }
При компиляции исходного кода Java аннотации могут обрабатываться подключаемыми модулями компилятора, называемыми процессорами аннотаций. Процессоры могут создавать информационные сообщения или создавать дополнительные исходные файлы или ресурсы Java, которые в свою очередь могут быть скомпилированы и обработаны. Однако процессоры аннотаций не могут изменять сам аннотированный код. (Модификации кода могут быть реализованы с использованием методов, выходящих за рамки спецификации языка Java.) Компилятор Java условно сохраняет метаданные аннотаций в файлах классов, если аннотация имеет RetentionPolicy
или CLASS
. RUNTIME
Позже JVM или другие программы могут искать метаданные, чтобы определить, как взаимодействовать с элементами программы или изменять их поведение.
В дополнение к обработке аннотации с помощью процессора аннотаций, программист Java может написать свой собственный код, который использует отражение для обработки аннотации. Java SE 5 поддерживает новый интерфейс, который определен в java.lang.reflect
пакете. Этот пакет содержит интерфейс AnnotatedElement
, который реализуется классами отражения Java, включая Class
, Constructor
, Field
, Method
, и Package
. Реализации этого интерфейса используются для представления аннотированного элемента программы, которая в данный момент выполняется в виртуальной машине Java. Этот интерфейс позволяет считывать аннотации рефлексивно.
Интерфейс AnnotatedElement
предоставляет доступ к аннотациям, имеющим RUNTIME
сохранение. Этот доступ предоставляется методами getAnnotation
, getAnnotations
и isAnnotationPresent
. Поскольку типы аннотаций компилируются и сохраняются в файлах байт-кода, как и классы, аннотации, возвращаемые этими методами, можно запрашивать так же, как и любой обычный объект Java. Полный пример обработки аннотации приведен ниже:
импорт java.lang.annotation.Retention ; // Это аннотация для обработки // Значение по умолчанию для Target — все элементы Java // Изменить политику хранения на RUNTIME (по умолчанию CLASS) @Retention ( RetentionPolicy . RUNTIME ) public @interface TypeHeader { // Значение по умолчанию, указанное для атрибута разработчика String developer () default "Unknown" ; String lastModified (); String [] teamMembers (); int meaningOfLife (); }
// Это аннотация, применяемая к классу @TypeHeader ( developer = "Bob Bee" , lastModified = "2013-02-12" , teamMembers = { "Ann" , "Dan" , "Fran" }, meaningOfLife = 42 ) public class SetCustomAnnotation { // Содержимое класса находится здесь }
// Это пример кода, который обрабатывает аннотацию import java.lang.annotation.Annotation ; import java.lang.reflect.AnnotatedElement ; public class UseCustomAnnotation { public static void main ( String [] args ) { Class < SetCustomAnnotation > classObject = SetCustomAnnotation . class ; readAnnotation ( classObject ); } static void readAnnotation ( AnnotatedElement element ) { try { System.out.println ( " Значения элемента аннотации : \n" ) ; if ( element.isAnnotationPresent ( TypeHeader.class ) ) { // getAnnotation возвращает тип аннотации Annotation singleAnnotation = element.getAnnotation ( TypeHeader.class ) ; TypeHeader header = ( TypeHeader ) singleAnnotation ; System.out.println ( "Разработчик : " + header.developer ( ) ) ; System.out.println ( " Последнее изменение : " + header.lastModified ( ) ) ; // teamMembers возвращены как String [] System.out.print ( " Члены команды: " ) ; for ( String member : header.teamMembers ( ) ) System.out.print ( member + " , " ) ; System.out.print ( " \ n " ) ; System.out.println ( " Смысл жизни : " + header.valueOfLife ( ) ) ; } } catch ( Exception exception ) { exception.printStackTrace ( ) ; } } }