JavaRush /Blog Jawa /Random-JV /Jawa @Annotations. Apa iku lan carane nggunakake?
SemperAnte
tingkat
Донецк

Jawa @Annotations. Apa iku lan carane nggunakake?

Diterbitake ing grup
Artikel iki ditujokake kanggo wong sing durung tau nggarap Anotasi, nanging pengin ngerti apa iku lan apa sing digunakake. Yen sampeyan duwe pengalaman ing wilayah iki, aku ora mikir yen artikel iki bakal nggedhekake kawruh sampeyan (lan, nyatane, aku ora ngupayakake tujuan kasebut). Kajaba iku, artikel kasebut ora cocog kanggo wong sing lagi miwiti sinau basa Jawa. Yen sampeyan ora ngerti apa iku Map<> utawa HashMap<> utawa ora ngerti apa tegese entri statis { } ing definisi kelas, utawa ora tau nggarap refleksi, sampeyan isih awal banget kanggo maca artikel iki lan coba ngerti apa sing diarani anotasi. Alat iki dhewe ora digawe kanggo nggunakake pamula, awit iku mbutuhake pemahaman ora tanggung dhasar saka interaksi kelas lan obyek (mratelakake panemume) (thanks kanggo komentar kanggo nuduhake perlu kanggo postscript iki). Jawa @Annotations.  Apa iku lan carane nggunakake?  - 1Dadi ayo miwiti. Anotasi ing Jawa minangka jinis label ing kode sing nggambarake metadata kanggo fungsi / kelas / paket. Contone, @Override Annotation sing kondhang, sing nuduhake yen kita bakal ngilangi metode kelas induk. Ya, ing tangan siji, bisa tanpa, nanging yen wong tuwa ora duwe metode iki, ana kemungkinan kita nulis kode kanthi sia-sia, amarga Cara tartamtu iki ora bisa diarani, nanging kanthi @Override Annotation, kompiler bakal ngandhani yen: "Aku ora nemokake cara kasebut ing wong tuwa ... ana sing reged ing kene." Nanging, Anotasi bisa nggawa luwih saka mung makna "kanggo linuwih": padha bisa nyimpen sawetara data sing bakal digunakake mengko.

Pisanan, ayo goleki anotasi paling gampang sing diwenehake dening perpustakaan standar.

(matur nuwun maneh kanggo komentar, ing wiwitan aku ora mikir yen blok iki dibutuhake) Kaping pisanan, ayo ngrembug babagan anotasi. Saben wong duwe 2 parameter utama sing dibutuhake :
  • Jinis panyimpenan (Retensi);
  • Jinis obyek sing dituduhake (Target).

Jinis panyimpenan

Miturut "jinis panyimpenan" tegese tataran ing ngendi anotasi "slamet" ing kelas. Saben anotasi mung nduweni siji "jinis retensi" sing bisa ditemtokake ing kelas RetentionPolicy :
  • SOURCE - anotasi mung digunakake nalika nulis kode lan ora digatekake dening compiler (yaiku, ora disimpen sawise kompilasi). Biasane digunakake kanggo preprocessors (conditionally), utawa instruksi kanggo compiler
  • KELAS - anotasi disimpen sawise kompilasi, nanging ora digatekake dening JVM (yaiku ora bisa digunakake nalika runtime). Biasane digunakake kanggo layanan pihak katelu sing ngemot kode sampeyan minangka aplikasi plug-in
  • RUNTIME minangka anotasi sing disimpen sawise kompilasi lan dimuat dening JVM (yaiku, bisa digunakake sajrone eksekusi program kasebut). Digunakake minangka tandha ing kode sing langsung mengaruhi eksekusi program (conto bakal dibahas ing artikel iki)

Jinis obyek ing ndhuwur sing dituduhake

Katrangan iki kudu dijupuk meh harfiah, amarga ... ing Jawa, anotasi bisa ditemtokake liwat apa wae (Lapangan, kelas, fungsi, lan sapiturute) lan kanggo saben anotasi dituduhake apa persis sing bisa ditemtokake. Ora ana maneh aturan "siji bab" ing kene; anotasi bisa ditemtokake ing ndhuwur kabeh sing kapacak ing ngisor iki, utawa sampeyan mung bisa milih unsur sing dibutuhake saka kelas ElementType :
  • ANNOTATION_TYPE - anotasi liyane
  • KONSTRUKTOR - konstruktor kelas
  • FIELD - lapangan kelas
  • LOCAL_VARIABLE - variabel lokal
  • METODE - metode kelas
  • PAKET - deskripsi paket paket
  • PARAMETER - parameter metode public void hello(@Annontation String param){}
  • TYPE - dituduhake ing ndhuwur kelas
Secara total, ing Java SE 1.8, perpustakaan basa standar nyedhiyakake 10 anotasi. Ing artikel iki kita bakal nliti sing paling umum (sing kasengsem ing kabeh? Sugeng rawuh ing Javadoc ):

@Nganti

Penylametan: SUMBER; Sasaran: METODE. Anotasi iki nuduhake yen cara nulis kasebut diwarisake saka kelas induk. Anotasi pisanan sing saben programmer Java anyar ditemokake nalika nggunakake IDE sing terus-terusan nyurung @Override iki. Asring, guru saka YouTube nyaranake: "mbusak supaya ora ngganggu," utawa: "ninggalake tanpa mikir apa sebabe." Nyatane, anotasi luwih migunani: ora mung ngidini sampeyan ngerti cara sing ditetepake ing kelas iki kanggo pisanan, lan sing wis ana wong tuwa (sing mesthi nambah keterbacaan kode sampeyan), nanging uga anotasi iki. serves minangka "poto-mriksa" sing padha ora mistaken nalika nemtokake fungsi overloaded.

@Dibuwang

Penylametan: Runtime; Target: KONSTRUKTOR, FIELD, LOCAL_VARIABLE, METODE, PAKET, PARAMETER, JENIS. Anotasi iki ngenali cara, kelas utawa variabel sing "ora usang" lan bisa uga dibusak ing versi prodhuk sing bakal teka. Anotasi iki biasane ditemoni dening wong sing maca dokumentasi API apa wae, utawa perpustakaan Java standar sing padha. Kadhangkala anotasi iki diabaikan amarga ... ora nyebabake kesalahan lan, ing asas, dhewe ora ngganggu urip. Nanging, pesen utama sing ditindakake anotasi iki yaiku "kita wis nggawe cara sing luwih trep kanggo ngetrapake fungsi iki, gunakake, aja nganggo sing lawas" - uga, utawa liya - "kita ganti jeneng fungsi kasebut, nanging iki dadi, kita ninggalake kanggo warisan...” (sing umume ora ala). Cekakipun, yen sampeyan ndeleng @Deprecated, iku luwih apik kanggo nyoba ora nggunakake apa iku macet liwat kajaba iku pancen perlu, lan bisa uga worth maneh maca dokumentasi kanggo ngerti carane tugas dileksanakake dening unsur deprecated saiki dileksanakake. Contone, tinimbang nggunakake Tanggal anyar ().getYear () dianjurake kanggo nggunakake Calendar.getInstance (). njaluk (Calendar.YEAR) .

@SuppressWarnings

Penylametan: SUMBER; Target: TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE Anotasi iki mateni output bebaya kompiler sing gegayutan karo unsur sing ditemtokake. Apa anotasi SOURCE dituduhake ing ndhuwur kolom, metode, kelas.

@Retensi

Penylametan: RUNTIME; Target: ANNOTATION_TYPE; Anotasi iki nemtokake "jinis panyimpenan" saka anotasi ing ndhuwur sing ditemtokake. Ya, anotasi iki digunakake malah kanggo awake dhewe ... sihir lan kabeh.

@sasaran

Penylametan: RUNTIME; Target: ANNOTATION_TYPE; Anotasi iki nemtokake jinis obyek sing bisa dituduhake anotasi sing digawe. Ya, lan uga digunakake kanggo sampeyan dhewe, wis biasa ... Aku iki ngendi kita bisa ngrampungake pambuka kanggo anotasi standar perpustakaan Jawa, amarga. liyane digunakake cukup arang lan, sanajan padha duwe keuntungan dhewe, ora saben wong kudu menehi hasil karo wong-wong mau lan ora perlu. Yen sampeyan pengin aku ngomong babagan anotasi tartamtu saka perpustakaan standar (utawa, bisa uga, anotasi kaya @NotNull lan @Nullable, sing ora kalebu ing STL), tulis ing komentar - salah siji pangguna sing apik bakal mangsuli sampeyan ana, utawa Aku bakal nalika aku ndeleng. Yen akeh wong njaluk sawetara jinis anotasi, aku uga bakal nambah menyang artikel.

Aplikasi praktis saka anotasi RUNTIME

Bener, aku mikir cukup obrolan teoritis: ayo nerusake latihan nggunakake conto bot. Contone, sampeyan pengin nulis bot kanggo sawetara jaringan sosial. Kabeh jaringan utama, kayata VK, Facebook, Discord, duwe API dhewe sing ngidini sampeyan nulis bot. Kanggo jaringan sing padha, wis ana perpustakaan sing ditulis kanggo nggarap API, kalebu ing Jawa. Mulane, kita ora bakal nyelidiki karya API utawa perpustakaan. Kabeh sing kudu kita ngerti ing conto iki yaiku bot kita bisa nanggapi pesen sing dikirim menyang chatting ing ngendi bot kita bener-bener ana. Yaiku, umpamane kita duwe kelas MessageListener kanthi fungsi:
public class MessageListener
{
    public void onMessageReceived(MessageReceivedEvent event)
    {
    }
}
Iku tanggung jawab kanggo ngolah pesen sing ditampa. Kabeh sing kita butuhake saka kelas MessageReceivedEvent yaiku senar pesen sing ditampa (contone, "Hello" utawa "Bot, hello"). Perlu dipikirake: ing macem-macem perpustakaan kelas kasebut diarani beda. Aku digunakake perpustakaan kanggo Discord. Dadi, kita pengin nggawe bot nanggepi sawetara perintah sing diwiwiti karo "Bot" (nganggo utawa tanpa koma - mutusake dhewe: kanggo pelajaran iki, kita bakal nganggep manawa ora ana koma ing kana). Yaiku, fungsi kita bakal diwiwiti kaya:
public void onMessageReceived(MessageReceivedEvent event)
{
    //Убираем чувствительность к регистру (БоТ, бОт и т.д.)
    String message = event.getMessage().toLowerCase();
    if (message.startsWith("бот"))
    {

    }
}
Lan saiki kita duwe akeh opsi kanggo ngleksanakake perintah iki utawa kasebut. Mesthi, pisanan sampeyan kudu misahake prentah saka argumen, yaiku, dibagi dadi array.
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
            //В случае если просто написать "Бот"
        }
    }
}
Ora ana cara kanggo ngindhari potongan kode iki, amarga misahake prentah saka argumen mesthi perlu. Nanging banjur kita duwe pilihan:
  • Apa yen (command.equalsIgnoreCase("..."))
  • Ngalih (perintah)
  • Nggawe cara liya kanggo ngolah ...
  • Utawa nggunakake bantuan saka Anotasi.
Lan saiki kita wis tekan bagian praktis nggunakake Anotasi. Ayo ndeleng kode anotasi kanggo tugas kita (bisa uga beda-beda, mesthi).
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();

}
Penting! Saben parameter diterangake minangka fungsi (kanthi kurung). Mung primitif, String , Enum bisa digunakake minangka paramèter . Sampeyan ora bisa nulis List<String> args(); - kesalahan. Saiki kita wis njlèntrèhaké Annotation, ayo nggawe kelas, nelpon 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();

    }
}
Wigati dicathet siji ora trep cilik: t.c. Saiki kita berjuang kanggo universalitas, kabeh fungsi kudu duwe dhaptar paramèter formal sing padha, dadi sanajan perintah kasebut ora ana argumen, fungsi kasebut kudu duwe parameter String [] args . Saiki kita wis nerangake 3 prentah: hello, bye, help. Saiki ayo ngowahi MessageListener kanggo nindakake iki. Kanggo penak lan kacepetan kerja, kita bakal langsung nyimpen printah ing 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
                //В случае если просто написать "Бот"
            }
        }
    }
}
Iku kabeh sing dibutuhake kanggo tim kita bisa kerja. Saiki nambahake prentah anyar ora anyar yen, dudu kasus anyar, sing jumlah argumen kudu diitung maneh, lan bantuan uga kudu ditulis maneh, nambahake baris anyar. Saiki, kanggo nambah prentah, kita mung kudu nambah fungsi anyar kanthi anotasi @Command ing kelas CommandListener lan mung - printah kasebut ditambahake, kasus dianggep, bantuan ditambahake kanthi otomatis. Pancen ora bisa dibantah manawa masalah iki bisa ditanggulangi kanthi cara liya. Ya, kabeh sing bisa ditindakake kanthi bantuan anotasi / refleksi bisa ditindakake tanpa ana, mung pitakonan yaiku penak, optimalitas lan ukuran kode, mesthine nempelake Anotasi ing endi wae ana petunjuk sing bisa digunakake. iku uga ora pilihan paling nyoto , ing kabeh sampeyan kudu ngerti nalika kanggo mungkasi =). Nanging nalika nulis API, Pustaka utawa program kang bisa kanggo mbaleni jinis padha (nanging ora persis padha) kode, anotasi temtu solusi optimal.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION