۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

نحوه توصیف کد جاوا با حاشیه نویسی

یاد بگیرید که چگونه از حاشیه نویسی جاوا برای مرتبط کردن ابرداده با کلاس ها، روش ها و سایر عناصر برنامه در برنامه های جاوا استفاده کنید.

یاد بگیرید که چگونه از حاشیه نویسی جاوا برای مرتبط کردن ابرداده با کلاس ها، روش ها و سایر عناصر برنامه در برنامه های جاوا استفاده کنید.

مواقعی وجود دارد که شما نیاز دارید فراداده یا داده‌هایی را که داده‌ها را توصیف می‌کنند، با کلاس‌ها، متدها یا عناصر دیگر در کد جاوا خود مرتبط کنید. برای مثال، تیم شما ممکن است نیاز به شناسایی کلاس های ناتمام در یک برنامه بزرگ داشته باشد. برای هر کلاس ناتمام، فراداده شامل نام توسعه‌دهنده مسئول تکمیل کلاس و تاریخ تکمیل مورد انتظار کلاس است.

قبل از جاوا ۵، نظرات تنها مکانیزم منعطف جاوا برای مرتبط کردن ابرداده با عناصر برنامه بود. اما چون کامپایلر آنها را نادیده می گیرد، نظرات در زمان اجرا در دسترس نیستند. و حتی اگر آنها در دسترس بودند، باید متن را تجزیه کنید تا موارد داده مهم را به دست آورید. بدون استاندارد کردن نحوه تعیین اقلام داده، ممکن است تجزیه آنها غیرممکن باشد.

جاوا ۵ با معرفی حاشیه نویسی، مکانیزم استانداردی برای مرتبط کردن ابرداده با عناصر مختلف برنامه، همه چیز را تغییر داد. این آموزش شما را با حاشیه نویسی جاوا آشنا می کند.

آنچه در این آموزش جاوا خواهید آموخت

  • چهار عنصر حاشیه نویسی جاوا
  • استفاده از @interface برای اعلام انواع حاشیه نویسی
  • انواع متا حاشیه نویسی و مشکل انعطاف پذیری
  • نحوه پردازش حاشیه نویسی
  • انواع حاشیه نویسی از پیش تعریف شده جاوا

عناصر حاشیه نویسی جاوا

یک حاشیه نویسی در جاوا از چهار عنصر تشکیل شده است:

  • یک مکانیسم @interface برای اعلام انواع حاشیه نویسی.
  • انواع متا حاشیه نویسی، که می توانید برای شناسایی عناصر برنامه کاربردی که یک نوع حاشیه نویسی روی آنها اعمال می شود، شناسایی طول عمر حاشیه (نمونه ای از یک نوع حاشیه نویسی) و موارد دیگر استفاده کنید.
  • پشتیبانی از پردازش حاشیه نویسی از طریق یک برنامه افزودنی به Java Reflection API و یک ابزار عمومی برای پردازش حاشیه نویسی.
  • انواع حاشیه نویسی استاندارد (از پیش تعریف شده) جاوا.

می‌آموزید که چگونه از هر یک از این عناصر در حاشیه‌نویسی‌های جاوا خود استفاده کنید.

استفاده از @interface برای اعلام انواع حاشیه نویسی

می توانید با مشخص کردن نماد @ بلافاصله پس از آن کلمه رزرو شده interface و یک شناسه، نوع حاشیه نویسی را اعلام کنید. به عنوان مثال، فهرست ۱ یک نوع حاشیه نویسی ساده را اعلام می کند که ممکن است از آن برای حاشیه نویسی کد ایمن رشته استفاده کنید.


public @interface ThreadSafe
{
}

پس از اعلام این نوع حاشیه نویسی، با افزودن نماد @ و به دنبال آن نام نوع به سربرگ های روش، پیشوند روش هایی را که برای رشته ای ایمن در نظر می گرفتید با نمونه هایی از این نوع قرار دهید.

>

فهرست ۲ مثال ساده ای را نشان می دهد که در آن روش main() به صورت @ThreadSafe حاشیه نویسی شده است.


public class AnnDemo
{
   @ThreadSafe
   public static void main(String[] args)
   {
   }
}

نمونه های

ThreadSafe هیچ فراداده ای به جز نام نوع حاشیه نویسی ارائه نمی دهند. با این حال، می‌توانید ابرداده‌ها را با افزودن عناصر به این نوع، که در آن یک element سرصفحه روشی است که در بدنه نوع حاشیه‌نویسی قرار داده شده است، ارائه دهید.

علاوه بر نداشتن متن کد، عناصر مشمول محدودیت‌های زیر نیز هستند:

  • سرصفحه روش نمی تواند پارامترها را اعلام کند.
  • سرصفحه روش نمی‌تواند یک بند پرتاب ارائه کند.
  • نوع برگشتی هدر روش باید یک نوع اولیه باشد (به عنوان مثال، intjava.lang.String، java.lang.Class، یک enum، یک نوع حاشیه نویسی، یا آرایه ای از یکی از این انواع. هیچ نوع دیگری را نمی توان برای نوع برگشتی تعیین کرد.

به عنوان مثالی دیگر، لیست ۳ یک نوع حاشیه نویسی ToDo را با سه عنصر برای شناسایی یک کار کدگذاری خاص، مشخص کردن تاریخ اتمام کار و نامگذاری رمزگذار مسئول تکمیل کار ارائه می کند. .


public @interface ToDo
{
   int id();
   String finishDate();
   String coder() default "n/a";
}

توجه داشته باشید که هر عنصر هیچ پارامتر(ها) یا بند پرتابی را اعلام نمی کند، دارای یک نوع بازگشتی قانونی است (int یا String)، و با یک نقطه ویرگول خاتمه می یابد. همچنین، عنصر نهایی نشان می دهد که یک مقدار بازگشتی پیش فرض می تواند مشخص شود. هنگامی که یک حاشیه نویسی مقداری را به عنصر اختصاص نمی دهد، این مقدار برگردانده می شود.

فهرست ۴ از ToDo برای حاشیه نویسی روش کلاس ناتمام استفاده می کند.


public class AnnDemo
{
   public static void main(String[] args)
   {
      String[] cities = { "New York", "Melbourne", "Beijing", "Moscow", 
                          "Paris", "London" };
      sort(cities);
   }

   @ToDo(id = 1000, finishDate = "10/10/2019", coder = "John Doe")
   static void sort(Object[] objects)
   {
   }
}

فهرست ۴ یک مورد فراداده را به هر عنصر اختصاص می دهد. برای مثال، ۱۰۰۰ به id اختصاص داده شده است. برخلاف coder، عناصر id و finishDate باید مشخص شوند. در غیر این صورت، کامپایلر یک خطا را گزارش خواهد کرد. وقتی به coder مقداری اختصاص داده نمی‌شود، مقدار پیش‌فرض "n/a" را در نظر می‌گیرد.

جاوا یک عنصر String value() ویژه ارائه می‌کند که می‌تواند برای برگرداندن فهرستی از موارد فوق داده با کاما از هم استفاده شود. فهرست ۵ این عنصر را در نسخه بازسازی شده ToDo نشان می دهد.


public @interface ToDo
{
   String value();
}

وقتی value() تنها عنصر یک نوع حاشیه نویسی است، لازم نیست هنگام تخصیص value و عملگر تخصیص = را مشخص کنید. یک رشته به این عنصر فهرست ۶ هر دو رویکرد را نشان می دهد.


public class AnnDemo
{
   public static void main(String[] args)
   {
      String[] cities = { "New York", "Melbourne", "Beijing", "Moscow", 
                          "Paris", "London" };
      sort(cities);
   }

   @ToDo(value = "1000,10/10/2019,John Doe")
   static void sort(Object[] objects)
   {
   }

   @ToDo("1000,10/10/2019,John Doe")
   static boolean search(Object[] objects, Object key)
   {
      return false;
   }
}

انواع متا حاشیه نویسی و مشکل انعطاف پذیری

می توانید انواع (مانند کلاس ها)، روش ها، متغیرهای محلی و موارد دیگر را حاشیه نویسی کنید. با این حال، این انعطاف پذیری می تواند مشکل ساز باشد. برای مثال، ممکن است بخواهید ToDo را فقط به روش‌ها محدود کنید، اما هیچ چیز مانع از استفاده آن برای حاشیه‌نویسی سایر عناصر برنامه نمی‌شود، همانطور که در فهرست ۷ نشان داده شده است.


@ToDo("1000,10/10/2019,John Doe")
public class AnnDemo
{
   public static void main(String[] args)
   {
      @ToDo(value = "1000,10/10/2019,John Doe")
      String[] cities = { "New York", "Melbourne", "Beijing", "Moscow", 
                          "Paris", "London" };
      sort(cities);
   }

   @ToDo(value = "1000,10/10/2019,John Doe")
   static void sort(Object[] objects)
   {
   }

   @ToDo("1000,10/10/2019,John Doe")
   static boolean search(Object[] objects, Object key)
   {
      return false;
   }
}

در فهرست ۷، ToDo همچنین برای حاشیه نویسی کلاس AnnDemo و متغیر محلی cities استفاده می شود. وجود این حاشیه‌نویسی‌های اشتباه ممکن است کسی را که کد شما یا حتی ابزارهای پردازش حاشیه‌نویسی شما را بررسی می‌کند سردرگم کند. برای مواقعی که باید انعطاف‌پذیری نوع حاشیه‌نویسی را محدود کنید، جاوا نوع حاشیه‌نویسی Target را در بسته java.lang.annotation خود ارائه می‌کند.

هدف یک نوع متا حاشیه نویسی است—یک نوع حاشیه نویسی برای انواع حاشیه نویسی. این با یک نوع غیر متا حاشیه نویسی متفاوت است، که حاشیه نویسی آن عناصر کاربردی مانند کلاس ها و روش ها را حاشیه نویسی می کند. نوع حاشیه نویسی Target انواع عناصر برنامه کاربردی را مشخص می کند که یک نوع حاشیه نویسی برای آنها قابل اعمال است. این عناصر با عنصر ElementValue[] value() Target شناسایی می شوند.

java.lang.annotation.ElementType یک عدد است که ثابت های آن عناصر برنامه را توصیف می کنند. برای مثال، CONSTRUCTOR برای سازنده ها و PARAMETER برای پارامترها اعمال می شود. فهرست کردن ۸ refactors فهرست کردن نوع حاشیه نویسی ToDo ۵ تا فقط به روش‌ها محدود شود.


import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
public @interface ToDo
{
   String value();
}

با توجه به نوع حاشیه نویسی اصلاح شده ToDo، تلاش برای کامپایل لیست ۷ اکنون به پیام خطای زیر منجر می شود:


AnnDemo.java:1: error: annotation type not applicable to this kind of declaration
@ToDo("1000,10/10/2019,John Doe")
^
AnnDemo.java:6: error: annotation type not applicable to this kind of declaration
      @ToDo(value="1000,10/10/2019,John Doe")
      ^
۲ errors

انواع meta-annotation اضافی

جاوا ۵ سه نوع مهم متا حاشیه نویسی را معرفی کرد که در بسته java.lang.annotation یافت می شوند:

  • حفظ نشان می دهد که حاشیه نویسی با نوع حاشیه نویسی چه مدت باید حفظ شود. enum java.lang.annotation.RetentionPolicy مرتبط با این نوع، ثابت های زیر را اعلام می کند:
    • CLASS: کامپایلر حاشیه نویسی ها را در یک فایل کلاس ثبت می کند و ماشین مجازی آنها را حفظ نمی کند. این خط مشی پیش فرض است
    • RUNTIME: کامپایلر حاشیه نویسی ها را در یک فایل کلاس ثبت می کند و ماشین مجازی آنها را حفظ می کند.
    • SOURCE: کامپایلر حاشیه نویسی را کنار می گذارد.
  • مستند شده نشان می‌دهد که نمونه‌هایی از حاشیه‌نویسی‌های مستند شده باید توسط javadoc و ابزارهای مشابه مستند شوند.
  • ارثی نشان می دهد که یک نوع حاشیه نویسی به طور خودکار به ارث می رسد.
  • CLASS: کامپایلر حاشیه نویسی ها را در یک فایل کلاس ثبت می کند و ماشین مجازی آنها را حفظ نمی کند. این خط مشی پیش فرض است
  • RUNTIME: کامپایلر حاشیه نویسی ها را در یک فایل کلاس ثبت می کند و ماشین مجازی آنها را حفظ می کند.
  • SOURCE: کامپایلر حاشیه نویسی را کنار می گذارد.

یک نوع متا حاشیه نویسی دیگر که در جاوا ۸ معرفی شده است، java.lang.annotation.Repeatable است. Repeatable برای نشان دادن اینکه نوع حاشیه نویسی که اعلان آن (meta-) حاشیه نویسی می کند، قابل تکرار است استفاده می شود. به عبارت دیگر، همانطور که در اینجا نشان داده شده است، می توانید چندین حاشیه نویسی را از یک نوع حاشیه نویسی تکرارپذیر به یک عنصر برنامه اعمال کنید:


@ToDo(value = "1000,10/10/2019,John Doe")
@ToDo(value = "1001,10/10/2019,Kate Doe")
static void sort(Object[] objects)
{
}

این مثال فرض می کند که ToDo با نوع حاشیه نویسی Repeatable حاشیه نویسی شده است.

نحوه پردازش حاشیه نویسی

حاشیه نویسی باید پردازش شود. در غیر این صورت، داشتن آنها فایده ای ندارد. جاوا ۵ Java Reflection API را گسترش داد تا به شما کمک کند ابزارهای پردازش حاشیه نویسی خود را ایجاد کنید. برای مثال، Class یک Annotation[]
متد getAnnotations()
که آرایه ای از نمونه های java.lang.Annotation را برمی گرداند. این نمونه ها حاشیه نویسی های موجود در عنصر توصیف شده توسط شی Class را توصیف می کنند.

فهرست ۹ یک برنامه کاربردی ساده را ارائه می‌کند که یک فایل کلاس را بارگیری می‌کند، روش‌های آن را برای حاشیه‌نویسی‌های ToDo بازجویی می‌کند، و اجزای هر حاشیه‌نویسی یافت شده را خروجی می‌دهد.


import java.lang.reflect.Method;

public class AnnProcDemo
{
   public static void main(String[] args) throws Exception
   {
      if (args.length != 1)
      {
         System.err.println("usage: java AnnProcDemo classfile");
         return;
      }
      Method[] methods = Class.forName(args[0]).getMethods();
      for (int i = 0; i < methods.length; i++)
      {
         if (methods[i].isAnnotationPresent(ToDo.class))
         {
            ToDo todo = methods[i].getAnnotation(ToDo.class);
            String[] components = todo.value().split(",");
            System.out.printf("ID = %s%n", components[0]);
            System.out.printf("Finish date = %s%n", components[1]);
            System.out.printf("Coder = %s%n%n", components[2]);
         }
      }
   }
}

پس از تأیید اینکه دقیقاً یک آرگومان خط فرمان (شناسایی یک فایل کلاس) مشخص شده است، main() فایل کلاس را از طریق Class.forName() بارگیری می کند، getMethods() را فراخوانی می کند تا آرایه ای از اشیاء java.lang.reflect.Method را که همه متدهای public را در فایل کلاس شناسایی می کند، برگرداند و اینها را پردازش می کند. روش ها.

پردازش روش با فراخوانی روش boolean isAnnotationPresent(Class annotationClass) برای تعیین اینکه آیا حاشیه نویسی توصیف شده توسط ToDo.class در روش وجود دارد یا خیر. اگر چنین است، روش T getAnnotation(Class
روش annotationClass)
برای به دست آوردن حاشیه نویسی فراخوانی می شود.

یادداشت‌های ToDo که پردازش می‌شوند، آنهایی هستند که انواع آنها یک عنصر String value() را اعلام می‌کنند (به فهرست ۵ مراجعه کنید). از آنجا که ابرداده مبتنی بر رشته این عنصر با کاما از هم جدا شده است، باید به آرایه ای از مقادیر مؤلفه تقسیم شود. سپس به هر یک از سه مقدار مؤلفه دسترسی یافته و خروجی می شود.

این کد منبع (javac AnnProcDemo.java) را کامپایل کنید. قبل از اینکه بتوانید برنامه را اجرا کنید، به یک فایل کلاس مناسب با حاشیه نویسی @ToDo در روش های public آن نیاز دارید. برای مثال، می‌توانید کد منبع فهرست ۶ AnnDemo را تغییر دهید تا public را در sort() و search() سرصفحه های روش. همچنین به نوع حاشیه نویسی ToDo Listing 10 نیاز دارید که به خط مشی حفظ RUNTIME نیاز دارد:


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ToDo
{
   String value();
}

AnnDemo.java و لیست ۱۰ تغییر یافته را کامپایل کنید و دستور زیر را برای پردازش حاشیه نویسی های AnnDemo ToDo اجرا کنید:


java AnnProcDemo AnnDemo

اگر همه چیز خوب پیش رفت، باید خروجی زیر را مشاهده کنید:


ID = 1000
Finish date = 10/10/2019
Coder = John Doe

ID = 1000
Finish date = 10/10/2019
Coder = John Doe

انواع حاشیه نویسی از پیش تعریف شده جاوا

جاوا ۵ همراه با انواع متا حاشیه نویسی هدف، Retention، مستند شده و ارثی معرفی شد. منسوخ شده، لغو و SuppressWarnings به عنوان انواع حاشیه نویسی از پیش تعریف شده. این سه نوع حاشیه نویسی فقط برای استفاده در زمینه کامپایلر طراحی شده اند، بنابراین سیاست های حفظ آنها روی SOURCE تنظیم شده است.

منسوخ شده

منسوخ شده عناصر برنامه را که قرار است منسوخ شوند، حاشیه نویسی می کند، به این معنی که آنها به تدریج حذف خواهند شد و نباید استفاده شوند. هنگامی که یک عنصر منسوخ شده در دسترس باشد، کامپایلر به شما هشدار می دهد.

سازنده java.util.Date زیر منسوخ شده را نشان می دهد:


@Deprecated
public Date(int year, int month, int date, int hrs, int min)
{
   // ... body of this constructor
}

جاوا ۹ یک جفت عنصر را در این نوع حاشیه نویسی معرفی کرد:

  • boolean forRemoval: نشان می دهد که آیا عنصر حاشیه نویسی در نسخه بعدی حذف می شود یا خیر. مقدار پیش فرض نادرست است.
  • رشته از: نسخه‌ای را برمی‌گرداند که در آن عنصر حاشیه‌نویسی منسوخ شده است. رشته نسخه در قالب و فضای نامی با مقدار تگ @since javadoc است. مقدار پیش فرض رشته خالی است.

این عناصر برای هشدار به توسعه‌دهندگان استفاده می‌شوند که بخش‌هایی از APIهای استاندارد جاوا برای حذف در نسخه جاوای آینده برنامه‌ریزی شده‌اند و به ترتیب در حال حذف شدن هستند. به عنوان مثال، جاوا ۹ از عنصر since برای شناسایی نسخه ای استفاده کرد که در آن java.io.FileInputStream و java.io.FileOutputStream‘ روش‌های void finalize() ابتدا منسوخ شدند:


@Deprecated(since="9")
protected void finalize() throws IOException

جاوا ۱۰ همچنین از عنصر forRemoval استفاده کرد تا نشان دهد که این روش‌های API در نسخه جاوا آینده حذف خواهند شد. این روش ها در جاوا ۱۲ حذف شدند:


@Deprecated(since="9",forRemoval=true)
protected void finalize() throws IOException

لغو

Override روش‌های زیر کلاسی را که همتایان سوپرکلاس خود را نادیده می‌گیرند، حاشیه‌نویسی می‌کند. هنگامی که متد subclass روش superclass را لغو نمی‌کند، کامپایلر یک خطا گزارش می‌کند.

مثال زیر Override را نشان می‌دهد که در آن روش public void run() رابط java.lang.Runnable توسط یک کلاس ناشناس لغو می‌شود:


Runnable r = new Runnable()
             {
                @Override
                public void run()
                {
                   // ... body of this method
                }
             };

SuppressWarnings

SuppressWarnings عناصر برنامه (و همه عناصر موجود در این عناصر برنامه) را که در آن هر یک از هشدارهای کامپایلر نام‌گذاری شده (به عنوان مثال، بررسی نشده) باید سرکوب شود، حاشیه‌نویسی می‌کند.

مثال زیر از SuppressWarnings برای سرکوب اخطار علامت‌نشده در زمینه بازیگران (E[]) استفاده می‌کند:


public class Container<E>
{
   private E[] elements;
   // ...

   @SuppressWarnings("unchecked")
   public Container(int size)
   {
      // ...
      elements = (E[]) new Object[size];
   }
   // ...
}

بدون حاشیه‌نویسی @SuppressWarnings("علامت‌گذاری نشده")، کامپایلر هشداری در مورد یک بازیگر انتخاب نشده ایجاد می‌کند. به طور خاص، (E[]) ارسال از Object[] به E[].

ممکن است تعجب کنید که چرا به جای عبارت حاوی بازیگران، سازنده را حاشیه نویسی کردم. من این کار را انجام دادم زیرا جاوا اجازه اعمال حاشیه نویسی روی عبارات را نمی دهد.

شروع به کار با ژنریک در جاوا

نحوهای E[] و در مثال قبلی با ویژگی زبان عمومی در جاوا.