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

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

Открыта

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

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

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

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

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

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

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

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

Комментарии (41)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
28 сентября 2023, 18:49
Хорошая будет игра, одобряю
Ra
Уровень 51
Student
30 июля 2023, 13:28
Вот так примерно инжектятся (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
Уровень 51
Student
30 июля 2023, 13:58
Аннотация (она такая же, как и @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, 15:05
какая халтура
wokku
Уровень 51
7 сентября 2023, 09:17
О чем не рассказали?
Макс Дудин
Уровень 41
14 июля 2022, 08:34
а вот тут зачем это всё и как может использоваться.. дальше есть одна задача где может понадобиться
Andrey Karelin
Уровень 41
14 мая 2022, 20:57
И все-таки как-то не раскрыта тема. Много внимания уделяется области действия, значениям, как получить и проверить. Но в последней лекции и задачах Аннотациями, по сути, заменили структуру наследования классов (парент-чайлд, или абстрактный - реализация). И если в наследовании все понятно и, собственно, в поведении обычных объектов с полями и методами, то что дает то, что вместо класса, мы все поля засунули в аннотации и оставили классы пустыми? Зачем нужно именно так....какие практические преимущества это дает, какие задачи решает? P.S. честно говоря, объяснения материала на основе ассоциаций с эльфами, орками и подобными игровыми персонажами, лично мне , не "игроману", понятно с легким туманом. Уж лучше на примере автомобилей, пицерий, заводов и фабрик).
LuneFox Java Developer в BIFIT Expert
10 марта 2022, 11:34
@Person(live=100, strength=10, magic=5, attack=20, defense=20)
class Elf {}
А кому нужна такая аннотация, если вдруг какой-то эльф или подвид эльфа будет в программе в будущем невероятно сильным с параметрами больше 9000? Зачем считывать аннотацию, если можно просто получить какое-нибудь значение непосредственно из поля класса? Или это частный случай, когда параметры эльфа чётко определены и никогда не изменятся?
Anton Stezhkin
Уровень 41
30 августа 2021, 14:49
"в игре очень много персонажей, которые делятся на три категории: эльфы, охрана дворца и злодей" - 😂 Где-то вдалеке послышалось мычание корованов...
LuneFox Java Developer в BIFIT Expert
10 марта 2022, 07:59
Я джва года ждал такой комментарий
Алексей
Уровень 41
1 февраля 2021, 12:22
О мои глаза! Как это прочитать и понять?! "Аннотация @Retention указывает, где будет видна наша аннотация: только в исходном коде, еще и после компиляции, будет доступна даже во время исполнения программ"
Илья Backend Developer в СберТех
18 февраля 2021, 13:35
согласен, так себе написание
Anton Stezhkin
Уровень 41
30 августа 2021, 17:08
Igor Petrashevsky
Уровень 47
28 августа 2022, 15:04
прям , очень хорошо. на 2 головы лучше JR
Dmitriy
Уровень 41
24 декабря 2020, 21:03
К слову, у Class (этот тот резидент java.lang, который от Object наследуется) есть метод getDeclaredAnnotation. И еще несколько в продолжение этой же темы.
Dmitriy
Уровень 41
24 декабря 2020, 20:40
Про типы аннотаций Target, если интересно, очень коротко - http://www.seostella.com/ru/article/2012/05/20/annotacii-v-java-target.html.