— Давай створимо зараз пару анотацій і використаємо їх.

Наприклад, ми пишемо ігровий рушій. Водночас у нашій грі дуже багато персонажів, які діляться на три категории: ельфи, охорона палацу і злодій.

Під час розробки гри можуть додаватися нові персонажі та, відповідно, змінюватися баланс гри. Тому було б дуже зручно приписати до кожного «класу персонажа» свою анотацію з описом його фізичних характеристик.

Тоді можна було б дуже просто симулювати бої між різними персонажами та/або швидко прорахувати баланс гри.

— Згоден, це слушна ідея.

— Давай створимо анотацію @Person, де будемо зберігати: життя, силу, магію, а також параметри атаки та захисту. Ось як би виглядала така анотація:

Приклад
@interface Person
{
 String name() default "";
 int live();
 int strength();
 int magic() default 0;
 int attack() default 0;
 int defense();
}

А ось такий вигляд мав би опис, наприклад, лісового ельфа-мага:

Приклад
@Person(live=100, strength=10, magic=5, attack=20, defense=20)
class Elf
{
 …
}

А ось такий вигляд мав би опис головного злодія:

Приклад
@Person(live=1000, strength=150, magic=250, attack=99, defense=99)
class EvilMaster
{
 …
}

— Ясно. Чимось нагадує інтерфейси-маркери.

— Так, але, по-перше, нема потреби щось успадковувати, по-друге, в анотаціях можна зберігати додаткову інформацію.

Є ще кілька анотацій, якими позначаються анотації. Ось вони:

Анотація @Retention вказує, де буде видно нашу анотацію: лише в початковому коді, ще й після компіляції, чи буде доступна навіть під час виконання програми.

Анотація @Target вказує, що саме можна позначити цією анотацією: клас, поле, метод, параметр методу тощо.

Якщо ми хочемо, щоб наша анотація діяла не лише на клас, який вона позначає, але й на його нащадків, треба позначити її @Inherited.

Ось як виглядатиме наша анотація @Person.

Пример
@Target(value=ElementType.TYPE)
@Retention(value=RetentionPolicy.RUNTIME)
@interface Person
{
 String name() default "";
 int live();
 int strength();
 int magic() default 0;
 int attack() default 0;
 int defense();
}

— Це було дуже цікаво, дякую!

А як працювати з цими анотаціями у програмі? Як їх використати? Як прочитати їх значення?

— Для цього використовують Reflection.

Ось як виглядало б визначення того, який із персонажів сильніший:

Приклад
public boolean fight(Class first, Class second)
{
 if (!first.isAnnotationPresent(Person.class))
  throw new RuntimeException("first param is not game person");
 if (!second.isAnnotationPresent(Person.class))
  throw new RuntimeException("second param is not game person");

 Person firstPerson = (Person) first.getAnnotation(Person.class);
 Person secondPerson = (Person) second.getAnnotation(Person.class);

 int firstAttack = firstPerson.attack() * firstPerson.strength() + firstPerson.magic();
 int firstPower = firstPerson.live() * firstPerson.defense() * firstAttack;

 int secondAttack = secondPerson.attack() * secondPerson.strength() + secondPerson.magic();
 int secondPower = secondPerson.live() * secondPerson.defense() * secondAttack;

 return firstPower > secondPower;
}

Ось методи класу, які нам потрібні:

Методи Опис
isAnnotationPresent(Annotation.class)
Перевіряє, чи є в класу необхідна анотація
getAnnotation(Annotation.class)
Повертає об'єкт-анотацію, якщо така є в класу
Annotation[] getAnnotations()
Повертає масив усіх анотацій класу

— Чудово. Я й подумати не міг, що отримати анотацію так просто.

— Ага. Просто виклич метод getAnnotation в об'єкта класу, і передай туди потрібний тобі тип анотації.

На цьому все!

— Дякую за дуже цікаву лекціію. Тепер я вже не боюся анотацій, як води.