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

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

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

Синтаксис

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

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, где так же есть подробные туториалы.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 32

    –4
    Атрибуты в C# немного удобнее и проще, как по мне… За статью спасибо!
      +16
      Все разработчики на Java дружно рады за вас!
        +1
        А было бы интересно провести сравнительный анализ аннотаций и атрибутов.
        +2
        Спасибо за статью, как раз изучаю Java и не понимал, что это, и зачем они нужны.

        P.S. заключите код в тег source, а то сливается с текстом ;)
          0
          спасибо, проглядел этот тег, поправил.
          0
          Спасибо, очень доходчиво обьяснили как работать с аннотациями. Ещё бы пару примеров для чего их стоит использоват в своих проектах, и для чего лучше не недо.
            +1
            Если кратко, то аннотации — это метаданные на уровне классов. Соответственно их используют, когда хотят вынести часть конфигурации непосредственно в код (как например в Spring и Hibernate), для декларативной валидации бинов (см. например здесь).
            В GWT аннотации активно используются в механизме локализации сообщений и привязки контролов к разметке в UiBinder.

            Еще один вариант кастомного использования — документирование. Например, у вас в руках есть сущности Hibernate и вы хотите с помощью hbm2doc документировать вашу БД. Вы можете зарегистрировать свою аннотацию @Documentation(""), навесить ее на интересующие поля в entity-классах и написать свой хук для этого экспортера, который будет сканировать классы на наличие аннотации.
            +3
            как доку прочитал. На мой взгляд такие статью надо писать с примерами посочнее.
              –1
              Посыпая голову пеплом, прочтя всё, что только возможно, но я так и не понял, ДЛЯ ЧЕГО нужны аннотации в Java.
              (Бросил Java за то, что язык стал другим и непонятным)
                +1
                Может Цейлон Вас заинтересует :)
                  0
                  В рантайме — для более простого маппинга данных/связывания объектов. Ну и аспекты потом прикрутили.
                    0
                    Интроспекция изначально считалось худшим, что возможно в ООП. Это нарушение принципов инкапсуляции ради эффективности. Если эффективность не может быть достигнута средствами ООП, то нужно просто брать и использовать другой язык, а не искажать объективную реальность данного.
                      +1
                      Это теория. На практике AspectJ менее удобен, чем Spring AOP.
                        0
                        Кем считалась? Интроспекция интроспекции рознь. Вот, например, контракт JavaBeans нарушает принципы инкапсуляции?
                      0
                      Например, можно использовать @Nullable и @NotNull для контроля значения полей. В IntelliJ IDEA очень на этот счет удобно — автоматические инспекции проверяют, может ли код отдать null и если аннотации совсем нет или указана @NotNull — будет показано нарушение такой-то инспекции, подсвечен подозрительный кусок.

                      Знаю об удобных инспекциях времени выполнения @ElementsNotNull для коллекций.
                      @MinValue и @MaxValue — или что-то близкое, — для контроля числовых значений.

                      В JUnit аннотации используются для отметки функций-тестов (@Test), правил (@Rule), группировки тестов в пакеты.
                      В общем, как видим, аннотациям можно придумать очень много интересных применений: метаданных очень удобная штука.
                        0
                        s/(метаданны)х/$1е/
                          0
                          Я когда эти @Nullable разрешил поставить в идее, она для этого подключила свой какой-то пакет. А мне почему-то не сильно нравится, когда IDE подключают свои либы. Переносимость на другие IDE становится тогда не шибко приятной. В команде работают как на NetBeans, Eclipse так и на Idea. Стараюсь бить по рукам, когда вижу зависимость от каких либо либ этих IDE. С развитием Мавена эта проблема конечно не так остра, но лично мне режет глаза, когда такие зависимости вижу.
                            +1
                            этот самый пакет, а именно annotations.jar, успешно присутствует в мавен репозитории и его можно просто подлючить и не насиловать свои глаза.
                              +1
                              есть jsr 305 jcp.org/en/jsr/detail?id=305
                              там описаны все эти @Nullable. По стандарту они лежат в javax.annotation, распознаются идеей, и наверное эклипсом тоже. Вполне универсальное средство на все IDE.
                                0
                                >удален
                              +1
                              В Eclipse тоже уже появилась поддержка @Nullable и @NonNull wiki.eclipse.org/JDT_Core/Null_Analysis
                                0
                                ага, и она соответственно зависит уже от пакета эклипса.
                                  +1
                                  В настройках Eclipse можно прописать полные имена аннотаций для null и not-null, взятых из любой библиотеки. Так что хоть по JSR.
                              +1
                              много для чего они нужны,
                              например сравните работы со стеком JEE технологий до того, как были введены в язык аннотации и после.
                              Что быстрее будет сделать, и чревато меньшим количество ошибок разработчика (даже из-за невнимательности),
                              делать xml файлы для конфигурирования бинов, или же просто заанотировать класс и нужные его поля?

                              Так же для примера можете вспомнить как работал JUnit до введения аннотаций (до 4 версии),
                              необходимо было поддерживать определенные названия тестовых методов, наследоваться от TestCase,
                              вроде ничего сложного, но сейчас тест намного быстрее написать, и осовение этой библиотеки, для тех кто учится,
                              я думаю стало проще.

                              Заметное упрощения для создания методов связывания данных и работы с ними уже упоминали выше в комментариях.
                                0
                                В современных системах код работает внутри контейнера или фреймворка. Контейнеру необходимо сообщить каким образом интегрировать этот код: нужна ли транзакция, какому полю в базе данных соответствует атрибут, и т.д. То есть помимо кода нужно иметь еще метаданные, описывающие его.

                                Раньше метаданные описывались отдельно от кода жуткими простынями xml. Было неудобно, но правильно. Затем изобрели XDoclet, который позволял прописывать метаданные прямо в коде при помощи специальных комментариев, а затем генерировал XML. Затем посчитали это полезным и решили ввести эту фишку прямо в язык ввиде аннотаций. Это удобно, но неправильно с точки зрения теории — код становится «завязан» на контейнер/фреймворк.

                                Я думаю, лучшим способом будет отказаться от конфигурации контейнера аннотациями и делать это на Java при помощи DSL. Например так делает Guice. В Scala есть интересная ORM Squeryl, которая использует схожий подход. Подобная ORM на Java называется QueryDSL.

                                P.S. Любителям аннотаций задача: есть модель данных и соответвующие ей джава бины. Нужно: с одной стороны замепить эту модель в базу данных, с другой — сделать вебсервис, работающий с ней. Все при условии, что код модели не дублировать. О том как подружить JPA и JAXB читайте на лучших девелоперских форумах… ))
                                  0
                                  Кроме уже опомянутого JUnit интересно аннотации используются в библиотеке Google Guice. Вот здесь неплохая статья про то, как инъекция зависимостей реализуется без, а потом уже с использованием аннотаций.
                                    0
                                    «Бросил Java» не очень сочетается с вашей аватаркой :)
                                    +9
                                    (hasStart==true)&&(hasStop==true)

                                    WTF?

                                    hasStart && hasStop
                                      –5
                                      мне показалось что так лучше будет читаться, чисто на глаз для восприятия,
                                      так что не вижу ничего страшного в таком коде, с учетом того, что это просто пример
                                        +1
                                        Вообще это считается моветоном: неоптимальный и бессмысленный код только загромождает место. Да и по смыслу получается масло маслянное.
                                          0
                                          вынужден согласиться про масло масляное,
                                          поправил, спасибо.
                                      0
                                      В статье слабо освещены варианты использования аннотаций.
                                      IMHO, имеет смысл добавить несколько примеров (вкратце), как они используются в современных framework'ах.

                                      Only users with full accounts can post comments. Log in, please.