چگونه با annotation ها کد جاوا را توضیح دهیم؟

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

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

جاوا 5، همه چیز را با معرفی annotaion ها (حاشیه ها)، استانداردی برای ارتباط متا داده با عناصر مختلف برنامه، تغییر داد. این مکانیزم از 4 المان تشکیل شده است:

  • یک مکانیزم interface@ برای تعریف انواع annotation ها.
  • نوع های Meta-annotation برای مشخص کردن طول عمر یک annotation به کار می روند.
  • پشتیبانی از پردازش annotation ها توسط Reflection API که می توانید annotation های یک برنامه در حال اجرا را پیدا کرده و ان ها را با استفاده از یک ابزرا پردازش کنید.
  • نوع های حاشیه نویسی (annotation) استاندارد.
annotation ها در جاوا

annotation ها در جاوا

تعریف انواع annotation ها در جاوا با interface@

برای ایجاد یک حاشیه، از سمبل @ به همراه کلمه کلیدی interface و سپس نام annotation مورد نظر خود، استفاده کنید. برای مثال، در کد زیر یک حاشیه ساده تعریف کرده ایم که می تواند برای حاشیه نویسی یک کد Thread-safe استفاده شود.

پس از تعریف annotation خود، برای استفاده از آن کافیست تا از نماد @ به همراه نامی که برای آن در نظر گرفته اید را به عنوان یک پیشوند قبل از عنصر مورد نظر خود (کلاس، متد، …)، قرار دهید. در کد زیر از حاشیه ای که در بالا ایجاد کرده بودیم، استفاده می کنیم.

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

از آن جایی که هیچ کدی را در بدنه annotation ها در جاوا نمی نویسیم، هدر یا بخش اعلان متد آن ها نیز با محدودیت هایی مواجه هستند:

  • هدر متد نمی تواند پارامتر داشته باشد
  • هدر متد نمی تواند دستور throw داشته باشد
  • مقدار برگشتی یک متد annotaion فقط باید یکی از انواع داده های اولیه (مانند اعداد صحیح)، نوع شمارشی، نوع annotation و یا آرایه ای از این نوع ها باشد.

کد زیر، یک حاشیه به نام ToDo را با سه عنصر نشان می دهد که یک کار خاص برنامه نویسی را مشخص می کند. تاریخ زمانی که کار مورد نظر به اتمام برسد و نام برنامه نویسی که مسئول انجام وظیفه مورد نظر است را مشخص می کند.

توجه داشته باشید که 3 هدر متدی که در بالا اعلان شد اند، تمامی شرط هایی که یک هدر باید داشته باشد را رعایت کرده اند. همچنین در متد آخر، مقدار برگشتی پیش فرض را مشخص کرده است. این مقدار پیش فرض هنگامی که annotation مقداری را به آن متد اختصاص ندهد، بازگشت داده می شود.

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

همانطور که میبینید، در این مثال به هر هدری که در بدنه حاشیه اعلان کردیم، یک مقدار تخصیص یافته است. برای مثال مقدار 1000 به id اختصاص بافته است. المان های id و finishDate، برخلاف coder حتما باید یک مقدار به آن ها تخصیص داده شود. در غیر این صورت، کامپایلر خطا خواهد داد. اگر به المان coder مقداری داده نشود، مقدار پیش فرض برای آن درنظر گرفته می شود.

جاوا یک المان ویژه ()String value فراهم کرده است که می تواند برای لیستی از متا داده ها را که با کاما “,” از هم جدا شده اند، برگرداند.

کد زیر نسخه refactor شده یا به اصطلاح بازآرایی شده حاشیه ToDo را نشان می دهد.

اگر هدر value، تنها هدری باشد که در بدنه حاشیه تعریف شده باشد، لازم نیست از نام value و عملگر = استفاده کنیم. بلکه نوشتن یک string که حاوی متا داده ها است، کافیست.

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

اختصاص annotation ها در جاوا به یک ساختار خاص (کلاس یا متد یا …)

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

در کد بالا، حاشیه ToDo هم برای کلاس AnnDemo و هم برای آرایه cities اعلان شده است. وجود این حاشیه نویسی اشتباه، ممکن است کسی که در حال بررسی کد است و یا حتی ابزار های پردازش annotation ها را نیز دچار سردرگمی کند.

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

در مثال زیر، حاشیه ToDo را فقط محدود به متد ها تعریف کرده ایم:

حالا اگر از حاشیه ToDo بر روی یک عنصر غیر متد، مانند متغیر تعریف کنیم، خطای زیر توسط کامپایلر تولید می شود: (خطا مربوط به 2 مثال قبلی است)

annotation ها در جاوا

annotation ها در جاوا

پردازش Annotation ها در جاوا

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

جاوا 5 با گسترش Reflection API، به شما در ایجاد ابزار های پردازش حاشیه ها، کمک می کند. به عنوان مثال، شئ Class یک متد به نام ()getAnnotations دارد که آرایه ای از حاشیه هایی را برمی گرداند که توسط شئ Class بر روی عنصر فعلی تعریف شده است.

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

برای اجرای هر برنامه جاوا، باید آن را از طریق خط فرمان (Command-line) سیستم عامل اجرا کنیم. حتی IDE ها نیز وقتی که دکمه اجرا را می زنید، برنامه را از طریق خط فرمان اجرا می کنند. در دستور خط فرمان از نام کلاس برای اجرا کردن آن استفاده می کنیم. در ادامه خواهید دید که ما برای اجرای کلاس فوق، نام یک کلاس را که حاوی حاشیه ToDo بر روی متد های خودش هست، به عنوان آرگومان به این کلاس ارجاع می دهیم. دلیل استفاده از Class.forName(args[0]) در برنامه بالا نیز همین است. در واقع داریم به شئ java.lang.Class کلاس AnnProcDemo اشاره می کنیم. سپس از طریق متد ()getMethods، به متد های این کلاس دسترسی داریم.

سپس در ادامه یک حلقه ایجاد کرده ایم که تمام متد های کلاس را بررسی می کند. اگر متدی دارای حاشیه ToDo باشد، عناصر آن حاشیه را چاپ می کند.

کد زیر نیز فایل حاوی تعریف حاشیه ToDo است:

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

فایل AnnDemo:

 

برای اجرای برنامه، دستور زیر را در خط فرمان اجرا کنید:

همانطور که می بینید، کلاس AnnDemo به عنوان یک آرگومان به کلاس AnnProcDemo ارسال شده است. با اجرای دستور بالا، متد ()main کلاس AnnProcDemo اجرا می شود.

خروجی به شکل زیر خواهد بود:

انوتیشن Deprecated@

این annotation برای مشخص کردن عناصر منسوخ شده (از رده خارج شده) استفاده می شود. از این عناصر دیگر نباید استفاده کرد. هنگام استفاده از چنین عناصری، کامپایلر هشدار خواهد داد.

در کد زیر، سازنده Date را Deprecated اعلام کرده ایم:

جاوا 9 دو المان را به این حاشیه اضافه کرده است.

  • boolean forRemoval : مشخص می کند که آیا عنصر مورد نظر در نسخه بعدی حذف خواهد شد یا خیر. مقدار پیش فرض آن false است. (این پارامتر برای زمانی که می خواهید به سایر توسعه دهندگان هشدار دهید که عنصر فعلی در نسخه بعد حذف خواهد شد و نباید از آن استفاده کنند، مفید است)
  • String since : نسخه ای که از آن جا به بعد ان عنصر منسوخ شده است را بر می گرداند. مقدار پیش فرض آن یک رشته خالی است.

مثال:

انوتیشن Override@

این حاشیه مشخص می کند که متد کلاس فرزند یا sub class، متد والد را به نفع خودش تغییر داده یا اصطلاحا Override می کند. اگر از این حاشیه استفاده کنید اما متد را Override نکنید، کامپایلر خطا خواهد داد.

در مثال زیر، متد ()run رابط Runnable توسط یک کلاس ناشناس override شده است:

انوتیشن SuppressWarnings@

این حاشیه برای نادیده گرفتن هشدار های کامپایلر برای عنصری (و تمام عناصر درون آن عنصر) که روی آن اعلان شده است، استفاده می شود.

کد زیر برنامه ای را نشان می دهد که از این حاشیه برای نادیده گرفتن هشدار بررسی نوع (type checking) را سرکوب می کند. ( تبدیل نوع []object به []E )

بدون نوشتن این حاشیه، کامپایلر هنگام کامپایل کردن کی هشدار برای type checking خواهد داد. همچنین به دلیل اینکه نمی توانیم annotation ها در جاوا را بر روی عبارات یا expression ها اعمال کنیم، آن را بر روی متد مربوطه قرار دادیم.

QR:  چگونه با annotation ها کد جاوا را توضیح دهیم؟
به اشتراک بگذارید