В языке компьютерного программирования Java аннотация представляет собой синтаксическую форму метаданные, которые можно добавить в исходный код Java . Классы, методы, переменные, параметры и Пакеты Java могут быть аннотированы. Как и теги Javadoc, аннотации Java можно читать из исходных файлов. В отличие от тегов Javadoc, аннотации Java также могут быть встроены и считаны из файлов классов Java, сгенерированных компилятором Java. Это позволяет сохранять аннотации виртуальной машиной Java во время времени выполнения и читать через отражение. В Java можно создавать мета-аннотации из существующих.
Платформа Java имеет различные специальные механизмы аннотации - например, модификатор transient
или тег @deprecated
javadoc. Запрос на спецификацию Java JSR-175 представил в 2002 году средство аннотации общего назначения (также известное как метаданные) для Java Community Process ; он получил одобрение в сентябре 2004 г. Аннотации стали доступны на самом языке, начиная с версии 1.5 пакета Java Development Kit (JDK). Инструмент apt
предоставил предварительный интерфейс для обработки аннотаций во время компиляции в JDK версии 1.5; JSR-269 формализовал это, и он был интегрирован в компилятор javac в версии 1.6.
Java определяет набор аннотаций, встроенных в язык. Из семи стандартных аннотаций три являются частью java.lang, а остальные четыре импортированы из java.lang.annotation.
Аннотации, применяемые к коду 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, как это желательно, из-за несоответствующий регистр. Если аннотация @Override
отсутствовала, в классе Cat был бы создан новый метод с именем gettype ()
.
общедоступный класс Animal {общедоступный void speak () {} общедоступный String getType () {return "Generic animal"; }} public class Cat extends Animal {@Override public void speak () {// Это хорошее переопределение. System.out.println ("Мяу."); } @Override public String gettype () {// Ошибка времени компиляции из-за опечатки: должно быть getType (), а не gettype (). return «Кошка»; }}
Объявления типа аннотации аналогичны обычным объявлениям интерфейса. Ат-знак (@) предшествует ключевому слову интерфейса. Каждое объявление метода определяет элемент типа аннотации. Объявления методов не должны иметь никаких параметров или предложения throws. Типы возвращаемых данных ограничены примитивами , String, Class, enums, аннотациями и массивами предыдущих типов. Методы могут иметь значения по умолчанию.
// @Twizzle - это аннотация к методу toggle (). @Twizzle public void toggle () {} // Объявляет аннотацию Twizzle. public @interface Twizzle {}
Аннотации могут включать необязательный список пар ключ-значение:
// То же, что и: @Edible (value = true) @Edible (true) Item item = new Carrot (); public @interface Edible {логическое значение () по умолчанию false; } @Author (first = "Oompah", last = "Loompah") Book book = new Book (); public @interface Автор {String first (); Строка last (); }
Сами аннотации могут быть аннотированы, чтобы указать, где и когда они могут использоваться:
@Retention (RetentionPolicy.RUNTIME) // Сделайте эту аннотацию доступной во время выполнения через отражение. @Target ({ElementType.METHOD}) // Эта аннотация может применяться только к методам класса. public @interface Tweezable {}
Компилятор резервирует набор специальных аннотаций (включая @Deprecated
, @Override
и @SuppressWarnings
) для синтаксических целей.
Аннотации часто используются фреймворками как способ удобного применения поведения к определяемым пользователем классам и методам, которые в противном случае должны быть объявлены во внешнем источнике (таком как файл конфигурации XML) или программно (с вызовами API). Следующее, например, представляет собой аннотированный класс данных JPA :
@Entity // Объявляет этот объектный компонент @Table (name = "people") // Сопоставляет bean-компонент с таблицей SQL "people "Открытый класс Person реализует Serializable {@Id // Сопоставьте его со столбцом первичного ключа. @GeneratedValue (strategy = GenerationType.AUTO) // База данных будет генерировать новые первичные ключи, а не мы. частный целочисленный идентификатор; @Column (length = 32) // Обрезать значения столбца до 32 символов. частное строковое имя; общедоступное целое число getId () {идентификатор возврата; } public void setId (Целочисленный идентификатор) {this.id = id; } public String getName () {возвращаемое имя; } public void setName (имя строки) {this.name = 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_public @PublicType. enum Priority {LOW, MEDIUM, HIGH} String value (); String changedBy () default ""; String lastChangedBy () default ""; Priority priority () по умолчанию Priority.MEDIUM; String createdBy () default «Джеймс Гослинг»; Строка lastChanged () по умолчанию «2011-07-08»; }
com.annotation пакета; public @interface UnderConstruction {String owner () default "Патрик Нотон"; Строковое значение () по умолчанию «Объект в разработке.»; String createdBy () по умолчанию «Майк Шеридан»; Строка lastChanged () по умолчанию «2011-07-08»; }
пакет com.validators; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.Validator; import 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") открытый класс DateValidator реализует Validator {public void validate (контекст FacesContext, компонент UIComponent, значение объекта) бросает ValidatorException {String date = (String) value; String errorLabel = "Пожалуйста, введите действительную дату."; если (! component.getAttributes (). isEmpty ()) {errorLabel = (String) component.getAttributes (). get ("errordisplayval"); } if (! 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); выбросить новое исключение ValidatorException (сообщение); }}}
Когда исходный код Java компилируется, аннотации могут обрабатываться подключаемыми модулями компилятора, называемыми процессорами аннотаций. Процессоры могут создавать информационные сообщения или создавать дополнительные исходные файлы или ресурсы Java, которые, в свою очередь, могут компилироваться и обрабатываться. Однако обработчики аннотаций не могут изменять сам аннотированный код. (Модификации кода могут быть реализованы с использованием методов, выходящих за рамки Спецификации языка Java.) Компилятор Java условно сохраняет метаданные аннотации в файлах классов, если аннотация имеет RetentionPolicy
из CLASS
или РАБОТА
. Позже JVM или другие программы могут искать метаданные, чтобы определить, как взаимодействовать с элементами программы или изменить их поведение.
Помимо обработки аннотации с помощью процессора аннотации, программист на Java может написать свой собственный код, который использует отражения для обработки аннотации. Java SE 5 поддерживает новый интерфейс, определенный в пакете java.lang.reflect
. Этот пакет содержит интерфейс под названием AnnotatedElement
, который реализуется классами отражения Java, включая Class
, Constructor
, Field
, Method
и Пакет
. Реализации этого интерфейса используются для представления аннотированного элемента программы, выполняющейся в настоящее время на виртуальной машине Java. Этот интерфейс позволяет рефлексивно читать аннотации.
Интерфейс AnnotatedElement
обеспечивает доступ к аннотациям с сохранением RUNTIME
. Этот доступ обеспечивается методами getAnnotation
, getAnnotations
и isAnnotationPresent
. Поскольку типы аннотаций компилируются и хранятся в файлах с байтовым кодом, как и классы, аннотации, возвращаемые этими методами, могут запрашиваться так же, как любой обычный объект Java. Полный пример обработки аннотации приведен ниже:
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; // Это аннотация, которая будет обработана // По умолчанию для Target - все элементы Java // Измените политику хранения на RUNTIME (по умолчанию - CLASS) @Retention (RetentionPolicy.RUNTIME) public @interface TypeHeader {// Значение по умолчанию, указанное для атрибута разработчика String developer () по умолчанию "Неизвестно"; Строка lastModified (); Строка teamMembers (); int valueOfLife (); }
// Это аннотация, применяемая к классу @TypeHeader (developer = "Bob Bee", lastModified = "2013-02-12", teamMembers = {"Ann", "Dan", "Fran"}, meanOfLife = 42) public class SetCustomAnnotation {// Содержимое класса находится здесь}
// Это пример кода, который обрабатывает аннотацию import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; открытый класс UseCustomAnnotation {общедоступный статический void main (String args) {ClassclassObject = SetCustomAnnotation.class; readAnnotation (classObject); } static void readAnnotation (элемент AnnotatedElement) {попробуйте {System.out.println ("Значения элемента аннотации: \ n"); if (element.isAnnotationPresent (TypeHeader.class)) {// getAnnotation возвращает тип аннотации Аннотация singleAnnotation = element.getAnnotation (TypeHeader.class); Заголовок TypeHeader = (TypeHeader) singleAnnotation; System.out.println ("Разработчик:" + header.developer ()); System.out.println ("Последнее изменение:" + header.lastModified ()); // teamMembers возвращается как String System.out.print ("Team members:"); для (член строки: header.teamMembers ()) System.out.print (член + ","); System.out.print ("\ п"); System.out.println («Смысл жизни:» + header.meaningOfLife ()); }} catch (исключение исключения) {exception.printStackTrace (); }}}
Исследователи изучили использование аннотаций Java в 1094 известных проектах Java с открытым исходным кодом, размещенных на GitHub. Они обнаружили, что аннотации активно поддерживаются, при этом многие аннотации добавляются, но также изменяются или удаляются из-за ошибок в типе или значениях аннотации. В целом, это исследование показывает, что существует небольшая, но значимая взаимосвязь между использованием аннотаций и вероятностью ошибок кода: Java-код с аннотациями, как правило, менее подвержен ошибкам.