В языке программирования Java аннотация представляет собой форму синтаксических метаданных , которые можно добавлять в исходный код Java . [1] Классы , методы , переменные , параметры и пакеты Java могут быть аннотированы. Как и теги Javadoc , аннотации Java можно читать из исходных файлов. В отличие от тегов Javadoc , аннотации Java также можно встраивать и читать из файлов классов Java , созданных компилятором Java . Это позволяет виртуальной машине Java сохранять аннотации во время выполнения и читать их посредством отражения . [2] Можно создавать метааннотации из существующих в Java. [3]
Платформа Java имеет различные механизмы специальных аннотаций, например transient
модификатор или @Deprecated
тег javadoc. Запрос на спецификацию Java JSR-175 представил возможность аннотаций общего назначения (также известных как метаданные ) в процессе сообщества Java в 2002 году; он получил одобрение в сентябре 2004 года. [4]
Аннотации стали доступны в самом языке, начиная с версии 1.5 Java Development Kit (JDK). Инструмент предоставил временный интерфейс для обработки аннотаций во время компиляции в JDK версии 1.5 apt
; JSR-269 формализовал это, и в версии 1.6 он был интегрирован в компилятор javac .
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, как хотелось бы, из-за несовпадения регистра . Если бы аннотация отсутствовала, в классе Cat был бы создан @Override
новый метод name .gettype()
общественный класс Animal { public void talk () { } public String getType () { return «Общее животное» ; } } public class Cat расширяет Animal { @Override public void talk () { // Это хорошее переопределение. Система . вне . println ( "Мяу." ); } @Override public String gettype () { // Ошибка времени компиляции из-за опечатки: должно быть getType(), а не gettype(). вернуть «Кот» ; } }
Объявления типов аннотаций аналогичны обычным объявлениям интерфейсов. Знак (@) предшествует ключевому слову «интерфейс».
// @Twizzle — это аннотация к методу toggle(). @Twizzle public void toggle () { } // Объявляет аннотацию Twizzle. общественный @interface Twizzle { }
Аннотации могут включать набор пар ключ-значение, которые моделируются как методы типа аннотации. Каждое объявление метода определяет элемент типа аннотации. Объявления методов не должны иметь никаких параметров или предложения throws. Типы возвращаемых значений ограничены примитивами , строками , классами, перечислениями , аннотациями и массивами предыдущих типов. Методы могут иметь значения по умолчанию .
// То же, что: @Edible(value = true) @Edible ( true ) Item item = new Carrot (); public @interface Edible { логическое значение () по умолчанию false ; } @Author ( first = "Oompah" , Last = "Looppah" ) Книга book = новая книга (); public @interface Author { Сначала строка (); Последняя строка (); }
Сами аннотации могут быть снабжены аннотациями, указывающими, где и когда их можно использовать:
@Retention ( RetentionPolicy . RUNTIME ) // Сделайте эту аннотацию доступной во время выполнения через отражение. @Target ({ ElementType . METHOD }) // Эту аннотацию можно применять только к методам класса. общественный @interface Tweezable { }
Компилятор резервирует набор специальных аннотаций (включая @Deprecated
, @Override
и @SuppressWarnings
) для синтаксических целей.
Аннотации часто используются платформами как способ удобного применения поведения к определяемым пользователем классам и методам, которые в противном случае должны быть объявлены во внешнем источнике (например, в файле конфигурации XML) или программно (с помощью вызовов API). Ниже приведен, например, аннотированный класс данных JPA :
@Entity // Объявляет этот объектный компонент @Table ( name = "people" ) // Сопоставляет компонент с таблицей SQL "people" public class Person реализует Serializable { @Id // Сопоставляет это со столбцом первичного ключа. @GeneratedValue ( Strategy = GenerationType . AUTO ) // База данных будет генерировать новые первичные ключи, а не мы. частный целочисленный идентификатор ; @Column ( length = 32 ) // Усекаем значения столбца до 32 символов. частное имя строки ; public Integer getId () { возвращаемый идентификатор ; } public void setId ( Целый идентификатор ) { this . идентификатор = идентификатор ; } public String getName () { возвращаемое имя ; } public void setName ( строковое имя ) { this . имя = имя ; } }
Аннотации не являются вызовами методов и сами по себе ничего не делают. Вместо этого объект класса передается реализации JPA во время выполнения , которая затем извлекает аннотации для создания объектно-реляционного отображения .
Полный пример приведен ниже:
пакет com.annotation ; импортировать java.lang.annotation.Documented ; импортировать java.lang.annotation.ElementType ; импортировать java.lang.annotation.Inherited ; импортировать java.lang.annotation.Retention ; импортировать java.lang.annotation.RetentionPolicy ; импортировать 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 Unfinished { public enum Priority { LOW , MEDIUM , HIGH } Строковое значение (); Строка [] изменена по () по умолчанию "" ; String [] lastChangedBy () default "" ; Приоритет Priority () Приоритет по умолчанию . СЕРЕДИНА ; Строка CreatedBy () по умолчанию "Джеймс Гослинг" ; Строка LastChanged () по умолчанию «2011-07-08» ; }
пакет com.annotation ; public @interface UnderConstruction { String Owner () default "Патрик Нотон" ; Строковое значение () по умолчанию «Объект находится в стадии строительства». ; Строка создано () по умолчанию "Майк Шеридан" ; Строка LastChanged () по умолчанию «2011-07-08» ; }
пакет com.validators ; импортировать javax.faces.application.FacesMessage ; импортировать javax.faces.comComponent.UIComponent ; импортировать javax.faces.context.FacesContext ; импортировать javax.faces.validator.Validator ; импортировать javax.faces.validator.ValidatorException ; импортировать com.annotation.UnderConstruction ; импортировать com.annotation.Unfinished ; импортировать com.annotation.Unfinished.Priority ; импортировать com.util.Util ; @UnderConstruction ( владелец = «Джон Доу» ) общедоступный класс DateValidator реализует Validator { public void validate ( контекст FacesContext , компонент UIComponent , значение объекта ) выдает ValidatorException { String date = ( String ) value ; String errorLabel = «Пожалуйста, введите правильную дату». ; если ( ! компонент . getAttributes (). isEmpty ()) { errorLabel = ( String ) компонент . Получить Атрибуты (). получить ( "ошибкадисплейвал" ); } if ( ! Util . validateAGivenDate ( date )) { @Unfinished ( changedBy = "Steve" , value = "добавлять сообщение в контекст или нет, подтвердите" , Priority = Priority . HIGH ) FacesMessage message = new FacesMessage (); сообщение . setSeverity ( FacesMessage . SEVERITY_ERROR ); сообщение . setSummary ( errorLabel ); сообщение . setDetail ( метка ошибки ); выдать новое исключение ValidatorException ( сообщение ); } } }
Когда исходный код Java компилируется, аннотации могут обрабатываться подключаемыми модулями компилятора, называемыми процессорами аннотаций. Процессоры могут создавать информационные сообщения или создавать дополнительные исходные файлы или ресурсы Java, которые, в свою очередь, могут быть скомпилированы и обработаны. Однако процессоры аннотаций не могут изменять сам аннотированный код. (Модификации кода могут быть реализованы с использованием методов, выходящих за рамки спецификации языка Java.) Компилятор Java условно сохраняет метаданные аннотации в файлах классов, если аннотация имеет RetentionPolicy
расширение CLASS
или RUNTIME
. Позже JVM или другие программы могут искать метаданные, чтобы определить, как взаимодействовать с элементами программы или изменить их поведение.
Помимо обработки аннотации с помощью процессора аннотаций, Java-программист может написать собственный код, использующий отражение для обработки аннотации. Java SE 5 поддерживает новый интерфейс, определенный в java.lang.reflect
пакете. Этот пакет содержит интерфейс AnnotatedElement
, который реализуется классами отражения Java, включая , Class
, Constructor
, и . Реализации этого интерфейса используются для представления аннотированного элемента программы, работающей в данный момент на виртуальной машине Java. Этот интерфейс позволяет рефлективно читать аннотации.Field
Method
Package
Интерфейс AnnotatedElement
обеспечивает доступ к аннотациям, имеющим RUNTIME
сохранение. Этот доступ обеспечивается методами getAnnotation
, getAnnotations
и isAnnotationPresent
. Поскольку типы аннотаций компилируются и сохраняются в файлах байт-кода, как и классы, аннотации, возвращаемые этими методами, можно запрашивать так же, как и любой обычный объект Java. Полный пример обработки аннотации приведен ниже:
импортировать java.lang.annotation.Retention ; импортировать java.lang.annotation.RetentionPolicy ; // Это аннотация, которая будет обработана // По умолчанию для цели — все элементы Java // Изменить политику хранения на RUNTIME (по умолчанию — CLASS) @Retention ( RetentionPolicy . RUNTIME ) public @interface TypeHeader { // Значение по умолчанию, указанное для атрибута разработчика Строка разработчика () по умолчанию «Неизвестно» ; Строка LastModified (); Строка [] TeamMembers (); int значениеOfLife (); }
// Это аннотация, применяемая к классу @TypeHeader ( developer = "Bob Bee" , LastModified = "2013-02-12" , teamMembers = { "Ann" , "Dan" , "Fran" }, значениеOfLife = 42 ) public class SetCustomAnnotation { // Здесь находится содержимое класса }
// Это пример кода, который обрабатывает импорт аннотаций java.lang.annotation.Annotation ; импортировать java.lang.reflect.AnnotatedElement ; общественный класс UseCustomAnnotation { public static void main ( String [ ] args ) { Class <SetCustomAnnotation> classObject = SetCustomAnnotation . сорт ; readAnnotation ( classObject ); } static void readAnnotation ( элемент AnnotatedElement ) { try { System . вне . println ( "Значения элементов аннотации: \n" ); if ( element.isAnnotationPresent ( TypeHeader.class )) { // getAnnotation возвращает тип аннотации Annotation singleAnnotation = element . getAnnotation ( TypeHeader . class ); Заголовок TypeHeader = ( TypeHeader ) SingleAnnotation ; Система . вне . println ( " Разработчик:" + заголовок.разработчик ( )); Система . вне . println ( "Последнее изменение:" + header.lastModified ( ) ); // члены команды возвращаются как String [] System . вне . print ( "Члены команды:" ); for ( Stringmember : header . teamMembers ( ) )) System . вне . печать ( член + ", " ); Система . вне . печать ( «\n» ); Система . вне . println ( "Смысл жизни:" + header . смыслOfLife ()); } } catch ( Исключение исключения ) { Exception . печатьStackTrace (); } } }