Аннотации в JAVA: обзор синтаксиса и создание собственных

Java
Sandbox
Статья ориентирована больше на новичков, или тех, кто еще не работал с данным механизмом в языке. Я постараюсь рассказать, что это такое, зачем они нужны, и как можно самому создавать удобные для себя аннотации.

Аннотации представляют из себя дескрипторы, включаемые в текст программы, и используются для хранения метаданных программного кода, необходимых на разных этапах жизненного цикла программы.
Информация, хранимая в аннотациях, может использоваться соответствующими обработчиками для создания необходимых вспомогательных файлов или для маркировки классов, полей и т.д.

Синтаксис

Аннотация задается описанием соответствующего интерфейса.
Например так:

import java.lang.annotation.*;
@Target(value=ElementType.FIELD)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface Name {
     String name();
     String type() default  “string”;
}


Как видно из примера выше, аннотация определяется описанием с ключевым словом interface и может включать в себя несколько полей, которые можно задать как обязательными, так и не обязательными. В последнем случае подставляется default значение поля.
Также из примера видно, что саму аннотацию можно пометить несколькими аннотациями.
Разберемся для начала, чем можно пометить собственную аннотацию, и зачем.

Аннотация @Retention позволяет указать жизненный цикл аннотации: будет она присутствовать только в исходном коде, в скомпилированном файле, или она будет также видна и в процессе выполнения. Выбор нужного типа зависит от того, как вы хотите использовать аннотацию, например, генерировать что-то побочное из исходных кодов, или в процессе выполнения стучаться к классу через reflection.

Аннотация @Target указывает, что именно мы можем пометить этой аннотацией, это может быть поле, метод, тип и т.д.

Аннотация @Documentedуказывает, что помеченная таким образом аннотация должна быть добавлена в javadoc поля/метода и т.д.
Например, класс, помеченный аннотацией без @Documented, будет выглядеть так:
public class TestClass
extends java.lang.Object


А если в описание аннотации добавить @Documented, получим:
@ControlledObject(name="name")
public class TestClass
extends java.lang.Object


Аннотация @Inherited помечает аннотацию, которая будет унаследована потомком класса, отмеченного такой аннотацией.
Сделаем для примера пару аннотаций и пометим ими класс.

@Inherited
@interface PublicAnnotate { }
 
@interface PrivateAnnotate { }
 
@PublicAnnotate
@PrivateAnnotate
class ParentClass { }
 
class ChildClass extends ParentClass { }

Класс ChildClass унаследует от родительского класса только аннотацию PublicAnnotate.

Пример своей аннотации.

Попробуем теперь написать рабочий пример с использованием аннотаций.
Представим себе, что у нас есть какой-то самодельный проект, который на вход получает класс, специально заанотированный, чтобы проект мог управлять жизненным циклом объектов этого класса, и пусть там будут аннотации StartObject, StopObject для описания методов класса, и ControlledObject для описания самого класса. Последней аннотации дадим еще поле name, путь там хранится якобы имя для поиска.
Аннотации будут выглядеть так:

@Target(value=ElementType.METHOD)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface StartObject {    
}

@Target(value=ElementType.METHOD)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface StopObject {    
}

@Target(value=ElementType.TYPE)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface ControlledObject {    
     String name();    
}


Напишем модуль, проверяющий подходит ли класс для загрузки в наш гипотетический проект или нет.
Сперва определим сам проверяемый класс.

@ControlledObject(name="biscuits")
public class Cookies {    
    
     @StartObject
     public void createCookie(){
       //бизнес логика
     }    
     @StopObject
     public void stopCookie(){
       //бизнес логика
     }
}


Для того, чтобы работать с классом, сначала необходимо загрузить класс в контекст приложения. Используем:
Class cl = Class.forName("org.annotate.test.classes.Cookies");
Далее, через механизм reflection мы получаем доступ к полям и аннотациям класса.
Проверим наличие аннотированных методов в классе и аннотации на самом классе:

if(!cl.isAnnotationPresent(ControlledObject.class)){
       System.err.println("no annotation");
       } else {
          System.out.println("class annotated ; name  -  " + cl.getAnnotation(ControlledObject.class));
       }        
boolean hasStart=false;
boolean hasStop=false;        
Method[] method = cl.getMethods();
for(Method md: method){
       if(md.isAnnotationPresent(StartObject.class)) {hasStart=true;}
       if(md.isAnnotationPresent(StopObject.class)) {hasStop=true;}
       if(hasStart && hasStop){break;}
}
System.out.println("Start annotaton  - " + hasStart + ";  Stop annotation  - " + hasStop );


Запустив, на выходе мы получим:
Start annotaton — true; Stop annotation — true.
Если попробовать убрать одну из аннотаций, то вывод сообщит о несоответствии требованиям.

Итог

Надеюсь, мне удалось показать, что аннотации предоставляют широкие возможности для работы с кодом программ, и имеет смысл не только пользоваться стандартными аннотациями языка, но и облегчать себе жизнь, создавая аннотации под свои нужды.

Подробнее ознакомиться с данным механизмом могу порекомендовать в книге «Java 2. Библиотека профессионала, том 2. Тонкости программирования» Кей С. Хорстманна, Гари Корнелла, или на сайте oracle, где так же есть подробные туториалы.
Tags:javaannotations
Hubs: Java
+26
207.7k 354
Comments 32

Popular right now

Java developer
from 240,000 to 270,000 ₽ОТП БанкМоскваRemote job
Java developer
from 150,000 ₽Sportmaster LabСанкт-ПетербургRemote job
Java-разработчик
from 180,000 to 250,000 ₽ЭвоторМоскваRemote job
Разработчик Java
from 200,000 ₽Банк «Открытие»Москва
Java-разработчик
from 40,000 to 150,000 ₽АО «ОТ-ОЙЛ» ВоронежRemote job

Top of the last 24 hours