JavaRush /مدونة جافا /Random-AR /جافا التعليقات التوضيحية. ما هو وكيفية استخدامه؟
SemperAnte
مستوى
Донецк

جافا التعليقات التوضيحية. ما هو وكيفية استخدامه؟

نشرت في المجموعة
هذه المقالة مخصصة للأشخاص الذين لم يسبق لهم التعامل مع التعليقات التوضيحية، ولكنهم يرغبون في فهم ماهيتها وفيم يتم استخدامها. إذا كانت لديك خبرة في هذا المجال، فلا أعتقد أن هذه المقالة ستوسع معرفتك بطريقة أو بأخرى (وفي الواقع، أنا لا أسعى إلى تحقيق هذا الهدف). كما أن المقالة غير مناسبة لأولئك الذين بدأوا للتو في تعلم لغة جافا. إذا كنت لا تفهم معنى Map<> أو HashMap<> أو لا تعرف ما يعنيه الإدخال الثابت{ } داخل تعريف الفئة، أو لم تتعامل مطلقًا مع الانعكاس، فمن السابق لأوانه قراءة هذه المقالة و حاول أن تفهم ما هي التعليقات التوضيحية. لم يتم إنشاء هذه الأداة نفسها للاستخدام من قبل المبتدئين، لأنها لا تتطلب فهمًا أساسيًا تمامًا لتفاعل الفئات والكائنات (رأيي) (بفضل التعليقات لإظهار الحاجة إلى هذا التذييل). جافا التعليقات التوضيحية.  ما هو وكيفية استخدامه؟  - 1اذا هيا بنا نبدأ. التعليقات التوضيحية في Java هي نوع من التسميات في التعليمات البرمجية التي تصف البيانات التعريفية لوظيفة/فئة/حزمة. على سبيل المثال، التعليق التوضيحيOverride الشهير، والذي يشير إلى أننا سنقوم بتجاوز طريقة من الفئة الأصل. نعم من ناحية ممكن بدونها لكن إذا لم يكن لدى الأهل هذه الطريقة فهناك احتمال أننا كتبنا الكود عبثا لأن قد لا يتم استدعاء هذه الطريقة أبدًا، ولكن باستخدام التعليق التوضيحي @Override، سيخبرنا المترجم بما يلي: "لم أجد مثل هذه الطريقة في الوالدين... هناك شيء قذر هنا." ومع ذلك، يمكن أن تحمل التعليقات التوضيحية أكثر من مجرد معنى "للموثوقية": يمكنها تخزين بعض البيانات التي سيتم استخدامها لاحقًا.

أولاً، دعونا نلقي نظرة على أبسط التعليقات التوضيحية التي تقدمها المكتبة القياسية.

(شكرًا مرة أخرى للتعليقات، في البداية لم أعتقد أن هناك حاجة لهذه الكتلة) أولاً، دعونا نناقش ما هي التعليقات التوضيحية. يحتوي كل واحد منهم على معلمتين رئيسيتين مطلوبتين :
  • نوع التخزين (الاحتفاظ)؛
  • نوع الكائن الذي يشار إليه (الهدف).

قوي

نعني بـ "نوع التخزين" المرحلة التي "يبقى" تعليقنا عليها داخل الفصل. يحتوي كل تعليق توضيحي على واحد فقط من "أنواع الاستبقاء" المحتملة المحددة في فئة RetentionPolicy :
  • المصدر - يتم استخدام التعليق التوضيحي فقط عند كتابة التعليمات البرمجية ويتم تجاهله بواسطة المترجم (أي لا يتم حفظه بعد التجميع). تُستخدم عادةً لأي معالجات مسبقة (مشروطة)، أو تعليمات للمترجم
  • CLASS - يتم الاحتفاظ بالتعليق التوضيحي بعد التجميع، ولكن يتم تجاهله بواسطة JVM (أي لا يمكن استخدامه في وقت التشغيل). يتم استخدامه عادةً لأي خدمات تابعة لجهات خارجية تقوم بتحميل التعليمات البرمجية الخاصة بك كتطبيق إضافي
  • RUNTIME هو تعليق توضيحي يتم حفظه بعد التجميع وتحميله بواسطة JVM (أي يمكن استخدامه أثناء تنفيذ البرنامج نفسه). تستخدم كعلامات في الكود تؤثر بشكل مباشر على تنفيذ البرنامج (مثال سيتم مناقشته في هذه المقالة)

نوع الكائن المشار إليه أعلاه

يجب أن يؤخذ هذا الوصف بشكل حرفي تقريبًا، لأنه... في Java، يمكن تحديد التعليقات التوضيحية على أي شيء (الحقول، الفئات، الوظائف، وما إلى ذلك) ولكل تعليق توضيحي تتم الإشارة إلى ما يمكن تحديده بالضبط. لم تعد هناك قاعدة "شيء واحد" هنا؛ يمكن تحديد تعليق توضيحي فوق كل شيء مدرج أدناه، أو يمكنك تحديد العناصر الضرورية فقط من فئة ElementType :
  • ANNOTATION_TYPE - تعليق توضيحي آخر
  • منشئ - منشئ الطبقة
  • المجال - مجال الفصل
  • LOCAL_VARIABLE - المتغير المحلي
  • الطريقة - طريقة الفصل
  • PACKAGE - وصف حزمة الحزمة
  • المعلمة - معلمة الطريقة public void hello(@Annontation String param){}
  • النوع - المشار إليه أعلى الفصل
في المجمل، اعتبارًا من Java SE 1.8، توفر لنا مكتبة اللغة القياسية 10 تعليقات توضيحية. في هذه المقالة سنلقي نظرة على أكثرها شيوعًا (من يهتم بها جميعًا؟ مرحبًا بك في Javadoc ):

@تجاوز

الاحتفاظ: المصدر؛ الهدف: الطريقة. يوضح هذا التعليق التوضيحي أن الطريقة التي تمت كتابتها بها موروثة من الفئة الأصل. التعليق التوضيحي الأول الذي يصادفه كل مبرمج Java مبتدئ عند استخدام IDE الذي يدفع هذه @Override باستمرار. في كثير من الأحيان، يوصي المعلمون من YouTube إما: "امسحه حتى لا يتداخل"، أو "اتركه دون أن تتساءل عن سبب وجوده هناك". في الواقع، يعد التعليق التوضيحي أكثر من مفيد: فهو لا يسمح لك فقط بفهم الأساليب التي تم تعريفها في هذا الفصل لأول مرة، والتي يمتلكها الآباء بالفعل (مما يزيد بلا شك من سهولة قراءة التعليمات البرمجية الخاصة بك)، ولكن أيضًا هذا التعليق التوضيحي بمثابة "فحص ذاتي" بأنك لم تكن مخطئًا عند تحديد وظيفة محمّلة بشكل زائد.

@إهمال

الاحتفاظ: وقت التشغيل؛ الهدف: المُنشئ، الحقل، LOCAL_VARIABLE، الطريقة، الحزمة، المعلمة، النوع. يحدد هذا التعليق التوضيحي الأساليب أو الفئات أو المتغيرات "القديمة" والتي قد تتم إزالتها في الإصدارات المستقبلية من المنتج. عادة ما يواجه هذا التعليق التوضيحي أولئك الذين يقرؤون وثائق أي واجهات برمجة التطبيقات (APIs)، أو نفس مكتبة Java القياسية. في بعض الأحيان يتم تجاهل هذا التعليق التوضيحي لأنه ... فهو لا يسبب أي أخطاء، ومن حيث المبدأ، في حد ذاته لا يتدخل كثيرا في الحياة. ومع ذلك، فإن الرسالة الرئيسية التي يحملها هذا التعليق التوضيحي هي "لقد توصلنا إلى طريقة أكثر ملاءمة لتنفيذ هذه الوظيفة، واستخدامها، ولا تستخدم الوظيفة القديمة" - حسنًا، وإلا - "لقد قمنا بإعادة تسمية الوظيفة، ولكن هذا لقد تركناها للإرث..." (وهو أيضًا ليس سيئًا بشكل عام). باختصار، إذا رأيت @Deprecated، فمن الأفضل أن تحاول عدم استخدام ما يعلق عليه إلا إذا كان ذلك ضروريًا للغاية، وقد يكون من المفيد إعادة قراءة الوثائق لفهم كيفية تنفيذ المهمة التي يؤديها العنصر المهمل الآن. على سبيل المثال، بدلاً من استخدام Date().getYear() الجديد ، يوصى باستخدام Calendar.getInstance().get(Calendar.YEAR) .

@قمع تحذيرات

الاحتفاظ: المصدر؛ الهدف: TYPE، FIELD، METHOD، PARAMETER، CONSTRUCTOR، LOCAL_VARIABLE يعمل هذا التعليق التوضيحي على تعطيل إخراج تحذيرات برنامج التحويل البرمجي التي تتعلق بالعنصر الذي تم تحديده عليه. هل التعليق التوضيحي SOURCE موضح أعلاه في الحقول والأساليب والفئات.

@حفظ

الاحتفاظ: وقت التشغيل؛ الهدف: ANNOTATION_TYPE؛ يحدد هذا التعليق التوضيحي "نوع التخزين" للتعليق التوضيحي الذي تم تحديده فوقه. نعم، يتم استخدام هذا الشرح حتى لنفسه... السحر وهذا كل شيء.

@هدف

الاحتفاظ: وقت التشغيل؛ الهدف: ANNOTATION_TYPE؛ يحدد هذا التعليق التوضيحي نوع الكائن الذي يمكن الإشارة إلى التعليق التوضيحي الذي نقوم بإنشائه عليه. نعم، وهي تُستخدم أيضًا لنفسك، اعتد عليها... أعتقد أن هذا هو المكان الذي يمكننا فيه إكمال مقدمتنا للتعليقات التوضيحية القياسية لمكتبة Java، لأنه نادرًا ما يتم استخدام الباقي، وعلى الرغم من أن لها فوائدها الخاصة، إلا أنه لا يتعين على الجميع التعامل معها وهي غير ضرورية على الإطلاق. إذا كنت تريد مني أن أتحدث عن تعليق توضيحي محدد من المكتبة القياسية (أو ربما التعليقات التوضيحية مثلNotNull و@Nullable، والتي لم يتم تضمينها في STL)، فاكتب في التعليقات - إما أن يجيبك المستخدمون الطيبون هناك، أو سأفعل عندما أراه. إذا طلب الكثير من الأشخاص نوعًا من التعليقات التوضيحية، فسأضيفها أيضًا إلى المقالة.

التطبيق العملي لشروح RUNTIME

في الواقع، أعتقد أن هذا يكفي من الثرثرة النظرية: فلننتقل إلى التدريب باستخدام مثال الروبوت. لنفترض أنك تريد كتابة روبوت لبعض الشبكات الاجتماعية. جميع الشبكات الرئيسية، مثل VK وFacebook وDiscord، لديها واجهات برمجة التطبيقات الخاصة بها والتي تسمح لك بكتابة روبوت. بالنسبة لهذه الشبكات نفسها، توجد بالفعل مكتبات مكتوبة للعمل مع واجهات برمجة التطبيقات، بما في ذلك Java. لذلك، لن نتعمق في عمل أي واجهة برمجة تطبيقات أو مكتبة. كل ما نحتاج إلى معرفته في هذا المثال هو أن الروبوت الخاص بنا يمكنه الرد على الرسائل المرسلة إلى الدردشة التي يوجد بها الروبوت بالفعل. لنفترض أن لدينا فئة messageListener مع وظيفة:
public class MessageListener
{
    public void onMessageReceived(MessageReceivedEvent event)
    {
    }
}
وهي مسؤولة عن معالجة الرسالة المستلمة. كل ما نحتاجه من فئة messageReceivedEvent هو سلسلة الرسالة المستلمة (على سبيل المثال، "Hello" أو "Bot, hello"). يجدر النظر: في المكتبات المختلفة يتم استدعاء هذه الفئات بشكل مختلف. لقد استخدمت المكتبة لـ 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
            //В случае если просто написать "Бот"
        }
    }
}
لا توجد طريقة يمكننا من خلالها تجنب هذا الجزء من التعليمات البرمجية، لأن فصل الأمر عن الوسائط ضروري دائمًا. ولكن بعد ذلك لدينا خيار:
  • افعل إذا (command.equalsIngnoreCase("..."))
  • هل التبديل (الأمر)
  • قم بطريقة أخرى للمعالجة ...
  • أو اللجوء إلى مساعدة التعليقات التوضيحية.
والآن وصلنا أخيرًا إلى الجزء العملي من استخدام التعليقات التوضيحية. دعونا نلقي نظرة على رمز التعليق التوضيحي لمهمتنا (قد يختلف بالطبع).
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();

    }
}
تجدر الإشارة إلى إزعاج صغير واحد: t.c. نحن الآن نناضل من أجل العالمية، يجب أن تحتوي جميع الوظائف على نفس قائمة المعلمات الرسمية، لذلك حتى لو لم يكن للأمر أي وسيطات، يجب أن تحتوي الوظيفة على معلمة 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 وهذا كل شيء - تتم إضافة الأمر، وتؤخذ الحالات في الاعتبار، وتتم إضافة المساعدة تلقائيًا. لا جدال على الإطلاق في أنه يمكن حل هذه المشكلة بعدة طرق أخرى. نعم، كل ما يمكن القيام به بمساعدة التعليقات التوضيحية/التأملات يمكن القيام به بدونها، والسؤال الوحيد هو الملاءمة والمثالية وحجم الكود، بالطبع، لصق تعليق توضيحي حيثما يكون هناك أدنى تلميح أنه سيكون من الممكن استخدامه كما أنه ليس الخيار الأكثر عقلانية، في كل ما تحتاج إلى معرفته متى تتوقف =). ولكن عند كتابة واجهات برمجة التطبيقات أو المكتبات أو البرامج التي من الممكن فيها تكرار نفس النوع (ولكن ليس نفس الكود)، فإن التعليقات التوضيحية هي بلا شك الحل الأمثل.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION