JavaRush /Курсы /Java Collections /Аннотации, как создавать

Аннотации, как создавать

Java Collections
8 уровень , 9 лекция
Открыта

— Так вот, давай-ка сейчас создадим парочку аннотаций и используем их.

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

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

Тогда можно было бы очень просто симулировать бои между различными персонажами и/или быстро просчитать баланс игры.

— Согласен – это хорошая идея.

— Давай создадим аннотацию @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 у объекта класса, и передал туда нужный тебе тип аннотации.

На этом на сегодня все.

— Спасибо, Риша, это была очень интересная лекция. И теперь я уже не боюсь аннотаций как воды.

Комментарии (42)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
28 сентября 2023
Хорошая будет игра, одобряю
Ra Уровень 61 Student
30 июля 2023
Вот так примерно инжектятся (Dependency Injection) свойства бинов (сервисов) в Спринге через самодельную аннотацию @Autowired Это у нас фабрика бинов, свойства бинов устанавливаются через их сеттеры (так делать не надо, это пример)

// DI контейнер
    Map<String, Object> singletons = new ConcurrentHashMap<>();
...
// код пропустим, тут ищутся все файлы классов сервисов с аннотацией @Component, 
// начиная с basePackage, создаются их инстансы и добавляются в мапу singletons.
    public void instantiate(String basePackage) {...}
...
//сетим свойства бинов с аннотацией @Autowired
public void populateProperties() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    for (Object object : singletons.values()) {
        for (Field field : object.getClass().getDeclaredFields()) {
            if (!field.isAnnotationPresent(Autowired.class)) {
                continue;
            }    
            for (Object dependency : singletons.values()) {
                if (!dependency.getClass().equals(field.getType())) {
                    continue;
                }    
                String setterName = "set" + field.getName().substring(0, 1).toUpperCase() +
                        field.getName().substring(1); // setPromotionsService
                System.out.println("Setter name = " + setterName);
                Method setter = object.getClass().getMethod(setterName, dependency.getClass());
                setter.invoke(object, dependency);
            }
        }
    }
}
А это сервис

@Component
public class ProductService {
    @Autowired
    private PromotionsService promotionsService;
И сам бин ProductService, и его свойство promotionsService инжектится здесь автоматом. Надо только создать фабрику и выполнить последовательно в начале приложения: instantiate(...); populateProperties() ;
Ra Уровень 61 Student
30 июля 2023
Аннотация (она такая же, как и @Component)

@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
Материалы: https://habr.com/ru/articles/419679/ https://github.com/kciray8/MyOwnSpring https://kciray8.github.io/KciTasks/App/src/# https://www.youtube.com/watch?v=THU10kYM5go https://www.youtube.com/watch?v=04odZ9s1Svo https://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat https://www.youtube.com/watch?v=BmBr5diz8WA&list=PL6yLoZ_3Y0HKGL3F7vv2SNSrA3TkbXtBX https://www.youtube.com/watch?v=zlJNlqMUD00&list=PL81zTpL449O1_1WD07XrmUcqzmeBoVHVG&index=9 https://www.youtube.com/watch?v=szI5sza6Wug&list=PLm2nrlGYLq1Uv1qYpqMC07JYoOXilEQZI&index=14
Igor Petrashevsky Уровень 47
28 августа 2022
какая халтура
wokku Уровень 51
7 сентября 2023
О чем не рассказали?
Макс Дудин Уровень 41
14 июля 2022
а вот тут зачем это всё и как может использоваться.. дальше есть одна задача где может понадобиться
Andrey Karelin Уровень 41
14 мая 2022
И все-таки как-то не раскрыта тема. Много внимания уделяется области действия, значениям, как получить и проверить. Но в последней лекции и задачах Аннотациями, по сути, заменили структуру наследования классов (парент-чайлд, или абстрактный - реализация). И если в наследовании все понятно и, собственно, в поведении обычных объектов с полями и методами, то что дает то, что вместо класса, мы все поля засунули в аннотации и оставили классы пустыми? Зачем нужно именно так....какие практические преимущества это дает, какие задачи решает? P.S. честно говоря, объяснения материала на основе ассоциаций с эльфами, орками и подобными игровыми персонажами, лично мне , не "игроману", понятно с легким туманом. Уж лучше на примере автомобилей, пицерий, заводов и фабрик).
LuneFox Уровень 41 Expert
10 марта 2022

@Person(live=100, strength=10, magic=5, attack=20, defense=20)
class Elf { … }
А кому нужна такая аннотация, если вдруг какой-то эльф или подвид эльфа будет в программе в будущем невероятно сильным с параметрами больше 9000? Зачем считывать аннотацию, если можно просто получить какое-нибудь значение непосредственно из поля класса? Или это частный случай, когда параметры эльфа чётко определены и никогда не изменятся?
Anton Stezhkin Уровень 41
30 августа 2021
"в игре очень много персонажей, которые делятся на три категории: эльфы, охрана дворца и злодей" - 😂 Где-то вдалеке послышалось мычание корованов...
LuneFox Уровень 41 Expert
10 марта 2022
Я джва года ждал такой комментарий
Алексей Уровень 41
1 февраля 2021
О мои глаза! Как это прочитать и понять?! "Аннотация @Retention указывает, где будет видна наша аннотация: только в исходном коде, еще и после компиляции, будет доступна даже во время исполнения программ"
Илья Уровень 41
18 февраля 2021
согласен, так себе написание
Anton Stezhkin Уровень 41
30 августа 2021
Igor Petrashevsky Уровень 47
28 августа 2022
прям , очень хорошо. на 2 головы лучше JR
Dmitriy Уровень 41
24 декабря 2020
К слову, у Class (этот тот резидент java.lang, который от Object наследуется) есть метод getDeclaredAnnotation. И еще несколько в продолжение этой же темы.
Dmitriy Уровень 41
24 декабря 2020
Про типы аннотаций Target, если интересно, очень коротко - http://www.seostella.com/ru/article/2012/05/20/annotacii-v-java-target.html.
Владимир Уровень 48
10 февраля 2026
ссылка на webarchive: Аннотации в Java. @Target