Головна користь від механізму анотацій — не у використанні вже готових стандартних анотацій у JDK. Втім, потреба у створенні власної анотації також виникає нечасто. Наприклад, якщо ми розробляємо велику систему або виділяємо окрему бібліотеку, то на архітектурному рівні реалізація власної анотації точно буде корисною.

Давай спробуємо створити анотацію.

Для цього створюємо файл, і замість class або interface пишемо @interface. Це й буде файл нашої анотації. Внутрішньою структурою анотація схожа з інтерфейсом.


public @interface Sum {
   int sum() default 0;
}

@interface вказує на те, що це анотація, default повідомляє, що параметр за замовчуванням повертатиме певне значення.

Ми створили анотацію і теоретично вже можемо її використовувати, але спочатку краще зайнятися її конфігуруванням.

Справа в тому, що без конфігурування нашу анотацію можна застосувати до будь-чого (до класів, способів, атрибутів тощо), а тому в її використанні нема особливо сенсу. Це може здатися дивним, але нашу анотацію потрібно анотувати іншими анотаціями!

Почнемо з @Target.

Анотація @Target (актуальна з Java 1.5) обмежує можливість застосування конфігурованої анотації. Для обмеження до певного рівня до анотації @Target ми маємо передати параметр, що вказує, для яких видів її можна застосовувати. Ось декілька видів анотацій, які часто використовуються:

@Target(ElementType.PACKAGE) для пакетів
@Target(ElementType.TYPE) для класів
@Target(ElementType.CONSTRUCTOR) для конструкторів
@Target(ElementType.METHOD) для методів
@Target(ElementType.FIELD) для атрибутів (змінних) класу
@Target(ElementType.PARAMATER) для параметрів методу
@Target(ElementType.LOCAL_VARIABLE) для локальних змінних

Якщо потрібна інструкція з кількома типами, можна передати кілька параметрів як масив:


@Target({ ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })

Наступний важливий елемент конфігурації – анотація @Retention.

Вона вкаже, в якому життєвому циклі коду наша анотація буде доступною:

RetentionPolicy.SOURCE Анотації, що анотовані за допомогою політики зберігання SOURCE, відкидаються під час виконання.
RetentionPolicy.CLASS Анотації, що анотовані за допомогою політики зберігання CLASS, записуються до файлу .class, але видаляються під час виконання.
RetentionPolicy.RUNTIME Анотації, що анотовані за допомогою політики зберігання RUNTIME, зберігаються під час виконання й можуть бути доступними в нашій програмі під час виконання.

Також для конфігурації можна використовувати ще декілька анотацій:

Анотація Значення
@Inherited Дозволяє класу-спадкоємцю реалізувати спадкування анотацій батьківського класу.
@Documented Анотацію буде поміщено до згенерованої документації javadoc.

Ось тепер давай спробуємо створити власну анотацію.

Створимо анотацію, яка буде анотувати класи й методи та містити інформацію про автора й версії написаного коду:


    @Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Info {
   String author() default "Author";
   String version() default "0.0";
}

Нашу анотацію ми можемо застосовувати до методів та класів. Метадані нашої анотації будуть доступними на етапі виконання програми. Зверни увагу на параметри нашої анотації. Ми можемо вкласти два параметри: ім'я та версію, але можемо не класти, і тоді за замовчуванням будуть значення, які ми вказали (default "Author" та default "0.0").

Варто відзначити, що ми можемо й не вказувати дефолтне значення для параметрів. У такому разі зазначення цього параметра стає обов'язковим.

Під час передачі параметрів також обов'язково вказати параметр, який ми передаємо через value = "value". Явне зазначення є обов'язковим завжди, навіть якщо параметр в анотації лише один.

Застосовуємо нашу анотацію до кількох класів:


@Info
public class MyClass1 {
   @Info
   public void myClassMethod() {}
}
 
@Info(version = "2.0")
public class MyClass2 {
   @Info(author = "Anonymous")
   public void myClassMethod() {}
}
 
@Info(author = "Anonymous", version = "2.0")
public class MyClass3 {
   @Info(author = "Anonymous", version = "4.0")
   public void myClassMethod() {}
}