JavaRush /Блоги Java /Random-TG /Java @Annotations. Ин чист ва чӣ тавр истифода бурдани он...
SemperAnte
Сатҳи
Донецк

Java @Annotations. Ин чист ва чӣ тавр истифода бурдани он?

Дар гурӯҳ нашр шудааст
Ин мақола барои одамоне пешбинӣ шудааст, ки ҳеҷ гоҳ бо Аннотацияҳо кор накардаанд, аммо мехоҳанд бифаҳманд, ки он чӣ аст ва бо чӣ истифода мешавад. Агар шумо дар ин самт таҷриба дошта бошед, ман фикр намекунам, ки ин мақола дониши шуморо то андозае васеъ кунад (ва, дар асл, ман чунин ҳадафро пайгирӣ надорам). Инчунин, мақола барои онҳое, ки нав ба омӯхтани забони Java шурӯъ мекунанд, мувофиқ нест. Агар шумо чӣ будани Map<> ё HashMap<>- ро нафаҳмед ё намедонед, ки вуруди статикӣ дар дохor таърифи синф чӣ маъно дорад ё ҳеҷ гоҳ бо мулоҳиза кор накарда бошед, барои шумо хондани ин мақола хеле барвақт аст ва кӯшиш кунед, ки фаҳмед, ки эзоҳҳо чист. Худи ин асбоб барои истифода аз ҷониби шурӯъкунандагон сохта нашудааст, зеро он фаҳмиши на комилан оддии ҳамкории синфҳо ва an objectҳоро талаб мекунад (фикри ман) (ба шарофати шарҳҳо барои нишон додани зарурати ин постскрипт). Java @Annotations.  Ин чист ва чӣ тавр истифода бурдани он?  - 1Пас биёед оғоз кунем. Эзоҳҳо дар Java як навъ тамғакоғазҳо дар code мебошанд, ки метамаълумотро барои функсия/синф/баста тавсиф мекунанд. Масалан, шарҳи маъруфи @Override, ки нишон медиҳад, ки мо усули синфи волидайнро бекор карданием. Бале, аз як тараф, бе ин ҳам мумкин аст, аммо агар волидон ин усулро надошта бошанд, эҳтимол дорад, ки codeро беҳуда навиштаем, зеро Ин усули мушаххасро ҳеҷ гоҳ номидан мумкин нест, аммо бо @Override Annotation тартибдиҳанда ба мо мегӯяд: "Ман чунин усулро дар волидайн наёфтам ... дар ин ҷо чизе ифлос аст." Аммо, эзоҳҳо метавонанд на танҳо маънои "барои эътимод" -ро дошта бошанд: онҳо метавонанд баъзе маълумотеро, ки баъдтар истифода мешаванд, нигоҳ доранд.

Аввалан, биёед соддатарин эзоҳҳоро аз ҷониби китобхонаи стандартӣ пешниҳод кунем.

(боз ташаккур ба шарҳҳо, дар аввал ман фикр намекардам, ки ин блок лозим аст) Аввалан, биёед муҳокима кунем, ки шарҳҳо чист. Ҳар яке аз онҳо 2 параметрҳои асосии зарурӣ доранд:
  • Навъи нигаҳдорӣ (нигоҳдорӣ);
  • Навъи an objectе, ки бар он нишон дода шудааст (Ҳадаф).

Навъи нигаҳдорӣ

Бо "навъи нигаҳдорӣ" мо марҳилаеро дар назар дорем, ки шарҳи мо дар дохor синф "зинда мемонад". Ҳар як эзоҳ танҳо яке аз "намудҳои нигоҳдорӣ"-и эҳтимолиро дорад, ки дар синфи RetentionPolicy нишон дода шудааст :
  • СОРЧА - эзоҳ танҳо ҳангоми навиштани code истифода мешавад ва аз ҷониби компилятор сарфи назар карда мешавад (яъне пас аз тартиб додан он захира карда намешавад). Одатан барои ҳама гуна коркардкунандагони пешазинтихоботӣ (шартӣ) ё дастурҳо ба компилятор истифода мешаванд
  • CLASS - эзоҳ пас аз ҷамъоварӣ нигоҳ дошта мешавад, аммо аз ҷониби JVM нодида гирифта мешавад (яъне дар вақти корӣ истифода намешавад). Одатан барои ҳама гуна хидматҳои тарафи сеюм, ки рамзи шуморо ҳамчун замимаи плагин бор мекунанд, истифода мешавад
  • RUNTIME эзоҳест, ки пас аз тартиб додан захира карда мешавад ва аз ҷониби JVM бор карда мешавад (яъне ҳангоми иҷрои худи барнома метавонад истифода шавад). Ҳамчун аломатҳо дар code истифода мешаванд, ки ба иҷрои барнома мустақиман таъсир мерасонанд (масалан дар ин мақола муҳокима карда мешавад)

Навъи an objectе, ки дар боло нишон дода шудааст

Ин тавсифро бояд тақрибан ба маънои айнан гирифт, зеро... дар Java, эзоҳҳоро дар болои ҳама чиз (Майдонҳо, синфҳо, функсияҳо ва ғ.) муайян кардан мумкин аст ва барои ҳар як эзоҳ нишон дода мешавад, ки маҳз дар кадоме онро муайян кардан мумкин аст. Дар ин ҷо дигар қоидаи "як чиз" вуҷуд надорад; эзоҳро дар болои ҳама чизҳои дар поён овардашуда муайян кардан мумкин аст ё шумо метавонед танҳо унсурҳои зарурии синфи ElementType -ро интихоб кунед :
  • ANOTATION_TYPE - шарҳи дигар
  • КОНСТРУКТОР - созандаи синф
  • FIELD - майдони синф
  • LOCAL_VARIABLE - тағирёбандаи маҳаллӣ
  • METHOD - усули синф
  • ПАКЕТ - тавсифи бастаи баста
  • PARAMETER - параметри методи public void hello(@Annontation String param){}
  • TYPE - дар болои синф нишон дода шудааст
Дар маҷмӯъ, аз Java SE 1.8, китобхонаи стандартии забон ба мо 10 эзоҳ медиҳад. Дар ин мақола мо маъмултарини онҳоро дида мебароем (ки ба ҳамаи онҳо таваҷҷӯҳ доранд? Хуш омадед ба Javadoc ):

@Override

Нигоҳдорӣ: SOURCE; Ҳадаф: МЕТОД. Ин эзоҳ нишон медиҳад, ки усуле, ки дар он навишта шудааст, аз синфи волидайн мерос гирифта шудааст. Аввалин шарҳе, ки ҳар як барномасози Java-и навкор ҳангоми истифодаи IDE дучор меояд, ки ин @Override-ро пайваста тела медиҳад. Аксар вақт, муаллимон аз YouTube тавсия медиҳанд: "онро тоза кунед, то халал нарасонад" ё "бе он ки чаро он ҷост, онро тарк кунед." Дар асл, тавзеҳот бештар муфид аст: он на танҳо ба шумо имкон медиҳад, ки фаҳмед, ки кадом усулҳо дар ин синф бори аввал муайян карда шудаанд ва кадоме аз волидон аллакай доранд (ки бешубҳа хониши codeи шуморо зиёд мекунад), балки ин эзоҳро низ ҳамчун "худсанҷӣ" хидмат мекунад, ки шумо ҳангоми муайян кардани функсияи изофа хато накардаед.

@Қабулшуда

Нигоҳдорӣ: Вақти иҷро; Ҳадаф: КОНСТРУКТОР, МАЙДОН, МАҲАЛЛӢ_ТАҒГИРКУНАНДА, УСУЛ, ПАКЕТ, ПАРАМЕТР, НАВ. Ин эзоҳ усулҳо, синфҳо ё тағирёбандаҳоро муайян мекунад, ки "кӯҳнашуда" ҳастанд ва метавонанд дар versionҳои ояндаи маҳсулот хориҷ карда шаванд. Ин эзоҳро одатан онҳое дучор мешаванд, ки ҳуҷҷатҳои ягон API ё ҳамон китобхонаи стандартии Java-ро мехонанд. Баъзан ин эзоҳ нодида гирифта мешавад, зеро... ба ягон хатой рох намедихад ва аслан худаш ба хаёт чандон халал намерасонад. Бо вуҷуди ин, паёми асосие, ки ин шарҳ медиҳад, ин аст, ки "мо барои татбиқи ин функсия як усули мувофиқтареро пайдо кардем, онро истифода баред, усули кӯҳнаро истифода набаред" - хуб ё дигар - "мо функсияро тағир додем, аммо ин ҳамин тавр аст, мо онро барои мерос гузоштаем...» (ки ин ҳам умуман бад нест). Хулоса, агар шумо @Deprecated -ро бубинед, беҳтар аст, ки кӯшиш кунед, ки он чизеро, ки дар он овезон аст, истифода набаред, агар он комилан зарур бошад ва барои фаҳмидани он ки чӣ гуна вазифаи иҷрошудаи унсури бекоршуда ҳоло иҷро карда мешавад, бояд ҳуҷҷатҳоро дубора хонед. Масалан, ба ҷои истифодаи нави Date().getYear() тавсия дода мешавад, ки Calendar.getInstance().get(Calendar.YEAR) .

@SuppressWarnings

Нигоҳдорӣ: SOURCE; Ҳадаф: TYPE, FIELD, METHOD, PARAMETRE, CONSTRUCTOR, LOCAL_VARIABLE Ин эзоҳ натиҷаи огоҳиҳои компиляторро, ки ба унсуре, ки дар он нишон дода шудааст, ғайрифаъол мекунад. Оё эзоҳоти SOURCE дар боло майдонҳо, усулҳо, синфҳо нишон дода шудааст.

@Нигоҳдорӣ

Нигоҳдорӣ: RUNTIME; Ҳадаф: ANNOTATION_TYPE; Ин эзоҳ "навъи нигаҳдорӣ" -и эзоҳро, ки дар боло нишон дода шудааст, муайян мекунад. Бале, ин эзоҳ ҳатто барои худ истифода мешавад ... ҷодугарӣ ва ин ҳама.

@Ҳадаф

Нигоҳдорӣ: RUNTIME; Ҳадаф: ANNOTATION_TYPE; Ин эзоҳ навъи an objectеро муайян мекунад, ки тавзеҳоте, ки мо дар он эҷод мекунем, нишон дода мешавад. Бале, ва он ҳам барои худ истифода мешавад, одат кун... Ба фикрам, дар ҳамин ҷо мо метавонем муқаддимаи худро бо annotationҳои стандартии китобхонаи Java анҷом диҳем, зеро боқимондаҳо хеле кам истифода мешаванд ва гарчанде ки онҳо манфиатҳои худро доранд, на ҳама бояд бо онҳо мубориза баранд ва тамоман нолозим аст. Агар шумо хоҳед, ки ман дар бораи шарҳи мушаххас аз китобхонаи стандартӣ сӯҳбат кунам (ё шояд эзоҳҳо ба монанди @NotNull ва @Nullable, ки ба STL дохил карда нашудаанд), дар шарҳҳо нависед - ё корбарони меҳрубон ба шумо дар он ҷо ҷавоб хоҳанд дод, ё Вақте ки ман онро мебинам. Агар бисёриҳо як навъ эзоҳро талаб кунанд, ман онро низ ба мақола илова мекунам.

Истифодаи амалии шарҳҳои RUNTIME

Дар асл, ман фикр мекунам, ки ин як сӯҳбати назариявӣ кофӣ аст: биёед бо истифода аз мисоли бот ба амалия мегузарем. Фарз мекунем, ки шумо мехоҳед барои ягон шабакаи иҷтимоӣ бот нависед. Ҳама шабакаҳои асосӣ, аз қабor VK, Facebook, Discord, API-ҳои худро доранд, ки ба шумо имкон медиҳанд бот нависед. Барои ҳамин шабакаҳо аллакай китобхонаҳои хаттӣ барои кор бо API, аз ҷумла дар Java мавҷуданд. Аз ин рӯ, мо ба кори ягон API ё китобхона намеравем. Танҳо дар ин мисол мо бояд донем, ки боти мо метавонад ба паёмҳое, ки ба чат фиристода мешавад, посух диҳад, ки боти мо воқеан ҷойгир аст. Яъне, биёед бигӯем, ки мо синфи MessageListener дорем бо функсия:
public class MessageListener
{
    public void onMessageReceived(MessageReceivedEvent event)
    {
    }
}
Он барои коркарди паёми қабулшуда масъул аст. Ба мо танҳо аз синфи MessageReceivedEvent лозим аст, ки сатри паёми қабулшуда аст (масалан, "Салом" ё "Бот, салом"). Инро ба назар гирифтан лозим аст: дар китобхонаҳои гуногун ин синфҳоро ба таври гуногун меноманд. Ман китобхонаро барои Discord истифода кардам. Ва аз ин рӯ, мо мехоҳем, ки бот ба баъзе фармонҳое, ки аз "Bot" оғоз мешаванд, вокуниш нишон диҳад (бо вергул ё бе вергул - худатон қарор кунед: ба хотири ин дарс, мо фикр мекунем, ки дар он ҷо вергул набояд бошад). Яъне, функсияи мо бо чизе ба монанди:
public void onMessageReceived(MessageReceivedEvent event)
{
    //Убираем чувствительность к регистру (БоТ, бОт и т.д.)
    String message = event.getMessage().toLowerCase();
    if (message.startsWith("бот"))
    {

    }
}
Ва холо мо барои ичрои ин ё он фармон вариантхои зиёде дорем. Бешубҳа, аввал шумо бояд фармонро аз далелҳои он ҷудо кунед, яъне онро ба массив тақсим кунед.
public void onMessageReceived(MessageReceivedEvent event)
{
    //Убираем чувствительность к регистру (БоТ, бОт и т.д.)
    String message = event.getMessage().toLowerCase();
    if (message.startsWith("бот"))
    {
        try
        {
            //получим массив {"Бот", "(команду)", "аргумент1", "аргумент2",... "аргументN"};
            String[] args = message.split(" ");
            //Для удобства уберем "бот" и отделим команду от аргументов
            String command = args[1];
            String[] nArgs = Arrays.copyOfRange(args, 2, args.length);
            //Получor command = "(команда)"; nArgs = {"аргумент1", "аргумент2",..."аргументN"};
            //Данный массив может быть пустым
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            //Вывод списка команд or Howого-либо messages
            //В случае если просто написать "Бот"
        }
    }
}
Ҳеҷ гуна роҳе нест, ки мо аз ин порчаи code канорагирӣ кунем, зеро ҷудо кардани фармон аз аргументҳо ҳамеша зарур аст. Аммо пас мо интихоби худро дорем:
  • Оё агар(command.equalsIngnoreCase("..."))
  • Гузариш кунед (фармон)
  • Як роҳи дигари коркардро иҷро кунед ...
  • Ё ба ёрии Аннотацияҳо муроҷиат кунед.
Ва ҳоло мо ниҳоят ба қисми амалии истифодаи Аннотацияҳо расидем. Биёед codeи эзоҳро барои вазифаи мо бубинем (албатта он метавонад фарқ кунад).
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//Указывает, что наша Аннотация может быть использована
//Во время выполнения через Reflection (нам How раз это нужно).
@Retention(RetentionPolicy.RUNTIME)

//Указывает, что целью нашей Аннотации является метод
//Не класс, не переменная, не поле, а именно метод.
@Target(ElementType.METHOD)
public @interface Command //Описание. Заметим, что перед interface стоит @;
{
    //Команда за которую будет отвечать функция (например "привет");
    String name();

     //Аргументы команды, использоваться будут для вывода списка команд
    String args();

     //Минимальное количество аргументов, сразу присвоor 0 (логично)
    int minArgs() default 0;

    //Описание, тоже для списка
    String desc();

     //Максимальное число аргументов. В целом не обязательно, но тоже можно использовать
    int maxArgs() default Integer.MAX_VALUE;

     //Показывать ли команду в списке (вовсе необязательная строка, но мало ли, пригодится!)
    boolean showInHelp() default true;

    //Какие команды будут считаться эквивалентными нашей
    //(Например для "привет", это может быть "Здаров", "Прив" и т.д.)
    //Под каждый случай заводить функцию - не рационально
    String[] aliases();

}
Муҳим! Ҳар як параметр ҳамчун функсия тавсиф карда мешавад (бо қавсҳо). Танҳо примитивҳо, String , Enum метавонанд ҳамчун параметр истифода шаванд . Шумо наметавонед List<String> args(); - хатогӣ. Акнун, ки мо Аннотацияро тавсиф кардем, биёед як синф эҷод кунем ва онро CommandListener номида бошем .
public class CommandListener
{
    @Command(name = "привет",
            args = "",
            desc = "Будь культурным, поздоровайся",
            showInHelp = false,
            aliases = {"здаров"})
    public void hello(String[] args)
    {
        //Какой-то функционал, на Ваше усмотрение.
    }

    @Command(name = "пока",
            args = "",
            desc = "",
            aliases = {"удачи"})
    public void bye(String[] args)
    {
         // Функционал
    }

    @Command(name = "помощь",
            args = "",
            desc = "Выводит список команд",
            aliases = {"help", "команды"})
    public void help(String[] args)
    {
        StringBuilder sb = new StringBuilder("Список команд: \n");
        for (Method m : this.getClass().getDeclaredMethods())
        {
            if (m.isAnnotationPresent(Command.class))
            {
                Command com = m.getAnnotation(Command.class);
                if (com.showInHelp()) //Если нужно показывать команду в списке.
                {
                    sb.append("Бот, ")
                       .append(com.name()).append(" ")
                       .append(com.args()).append(" - ")
                       .append(com.desc()).append("\n");
                }
            }
        }
        //Отправка sb.toString();

    }
}
Як нороҳатии хурдро қайд кардан бамаврид аст: т. Мо ҳоло барои универсалӣ мубориза мебарем, ҳамаи функсияҳо бояд як рӯйхати параметрҳои расмӣ дошта бошанд, аз ин рӯ ҳатто агар фармон ягон далел надошта бошад, функсия бояд параметри String[] args дошта бошад . Мо ҳоло 3 фармонро тавсиф кардем: салом, хайр, кӯмак. Акнун биёед MessageListener-и худро тағир диҳем , то бо ин коре кунем. Барои роҳат ва суръати кор, мо фавран фармонҳои худро дар HashMap нигоҳ медорем :
public class MessageListner
{
    //Map который хранит How ключ команду
    //А How meaning функцию которая будет обрабатывать команду
    private static final Map<String, Method> COMMANDS = new HashMap<>();

    //Объект класса с командами (по сути нужен нам для рефлексии)
    private static final CommandListener LISTENER = new CommandListener();

    static
    {
       //Берем список всех методов в классе CommandListener
        for (Method m : LISTENER.getClass().getDeclaredMethods())
        {
            //Смотрим, есть ли у метода нужная нам Аннотация @Command
            if (m.isAnnotationPresent(Command.class))
            {
                //Берем an object нашей Аннотации
                Command cmd = m.getAnnotation(Command.class);
                //Кладем в качестве ключа нашей карты параметр name()
                //Определенный у нашей аннотации,
                //m — переменная, хранящая наш метод
                COMMANDS.put(cmd.name(), m);

                //Также заносим каждый элемент aliases
               //Как ключ указывающий на тот же самый метод.
                for (String s : cmd.aliases())
                {
                    COMMANDS.put(s, m);
                }
            }
        }
    }

    public void onMessageReceived(MessageReceivedEvent event)
    {

        String message = event.getMessage().toLowerCase();
        if (message.startsWith("бот"))
        {
            try
            {
                String[] args = message.split(" ");
                String command = args[1];
                String[] nArgs = Arrays.copyOfRange(args, 2, args.length);
                Method m = COMMANDS.get(command);
                if (m == null)
                {
                    //(вывод помощи)
                    return;
                }
                Command com = m.getAnnotation(Command.class);
                if (nArgs.length < com.minArgs())
                {
                    //что-то если аргументов меньше чем нужно
                }
                else if (nArgs.length > com.maxArgs())
                {
                    //что-то если аргументов больше чем нужно
                }
                //Через рефлексию вызываем нашу функцию-обработчик
                //Именно потому что мы всегда передаем nArgs у функции должен быть параметр
                //String[] args — иначе она просто не будет найдена;
                m.invoke(LISTENER, nArgs);
            }
            catch (ArrayIndexOutOfBoundsException e)
            {
                //Вывод списка команд or Howого-либо messages
                //В случае если просто написать "Бот"
            }
        }
    }
}
Барои кори коллективхои мо хамаи он чиз лозим аст. Ҳоло илова кардани фармони нав чизи нав нест, агар на парвандаи нав, ки дар он шумораи аргументҳо бояд дубора ҳисоб карда шаванд ва кӯмак низ бояд аз нав навишта шавад ва ба он сатрҳои нав илова карда шавад. Ҳоло, барои илова кардани фармон, мо бояд танҳо як функсияи навро бо шарҳи @Command дар синфи CommandListener илова кунем ва ин аст - фармон илова карда мешавад, ҳолатҳо ба назар гирифта мешаванд, кӯмак ба таври худкор илова карда мешавад. Комилан бахснопазир аст, ки ин проблемаро бо роххои дигар хал кардан мумкин аст. Бале, ҳама чизеро, ки бо ёрии эзоҳҳо/рефлексияҳо анҷом додан мумкин аст, бе онҳо анҷом додан мумкин аст, саволи ягона ин роҳатӣ, оптималӣ ва андозаи code аст, албатта, дар ҳама ҷое, ки каме ишорае вуҷуд дорад, ки онро истифода бурдан мумкин аст. он инчунин варианти оқилонатарин нест, дар ҳама чиз ба шумо лозим аст, ки донед, ки кай қатъ кардан лозим аст =). Аммо ҳангоми навиштани API-ҳо, Китобхонаҳо ё барномаҳое, ки дар онҳо codeи як навъ (вале на маҳз якхела) такрор кардан мумкин аст, эзоҳҳо бешубҳа ҳалли беҳтарин мебошанд.
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION