آنوتیشنها (حاشیهنویسیها / یادداشتهای کد) کاربردهای متعددی دارند، از جمله:
اطلاعات برای کامپایلر: آنوتیشنها میتوانند توسط کامپایلر برای تشخیص خطاها یا صرفنظر کردن از هشدارها (suppress warnings) استفاده شوند.
پردازش در زمان کامپایل و زمان انتشار (Deployment): ابزارهای نرمافزاری میتوانند اطلاعات آنوتیشنها را پردازش کرده تا کد، فایلهای XML و موارد دیگر را تولید کنند.
پردازش در زمان اجرا (Runtime): برخی از آنوتیشنها در زمان اجرای برنامه نیز قابل بررسی و تحلیل هستند.
این بخش توضیح میدهد که آنوتیشنها در چه جاهایی قابل استفاده هستند، چگونه میتوان آنها را بهکار برد، چه انواع آنوتیشنهای پیشفرضی در پلتفرم جاوا نسخه استاندارد (Java SE API) وجود دارند، چگونه آنوتیشنهای نوع (Type Annotations) میتوانند همراه با سیستمهای نوعِ قابلافزودن (Pluggable Type Systems) برای نوشتن کدهایی با بررسی دقیقترِ نوعها استفاده شوند، و در نهایت چگونه میتوان آنوتیشنهای تکرارشونده را پیادهسازی کرد.
در سادهترین حالت، یک آنوتیشن به شکل زیر است:
@Entity
نویسه یا کاراکتر آتساین (@) به کامپایلر نشان میدهد آنچه در ادامه میآید یک آنوتیشن است. در مثال زیر، نام آنوتیشن Override است:
@Override
void mySuperMethod() { ... }
آنوتیشنها میتوانند شامل المانهایی (عناصری) باشند که ممکن است نامگذاری شده یا بدون نام باشند، و برای این المانها مقادیری تعیین میشود:
@Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass { ... }
یا:
@SuppressWarnings(value = "unchecked")
void myMethod() { ... }
اگر تنها یک المان وجود داشته باشد که نام آن value است، میتوان از نوشتن نام آن صرفنظر کرد، مانند:
@SuppressWarnings("unchecked")
void myMethod() { ... }
اگر آنوتیشن هیچ المانی نداشته باشد، میتوان پرانتزها را نیز حذف کرد؛ همانطور که در مثال قبلی @Override نشان داده شد.
همچنین میتوان از چند آنوتیشن روی یک اعلان (Declaration) استفاده کرد:
@Author(name = "Jane Doe")
@EBook
class MyClass { ... }
اگر آنوتیشنها از یک نوع باشند، به آن آنوتیشن تکرارشونده (Repeating Annotation) میگویند:
@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }
آنوتیشنهای تکرارشونده از نسخه Java SE 8 به بعد پشتیبانی میشوند. برای اطلاعات بیشتر، بخش Repeating Annotations را ببینید.
نوع آنوتیشن میتواند یکی از انواع تعریفشده در پکیجهای java.lang یا java.lang.annotation از API استاندارد جاوا (Java SE) باشد. در مثالهای قبلی، Override و SuppressWarnings از آنوتیشنهای پیشفرض جاوا بودند. همچنین این امکان وجود دارد که نوع آنوتیشن اختصاصی خود را تعریف کنید؛ آنوتیشنهای Author و Ebook در مثالهای بالا از نوع آنوتیشنهای سفارشی (Custom) هستند.
آنوتیشنها را میتوان روی اعلانها (Declarations) اعمال کرد؛ اعلانِ کلاسها، فیلدها، متدها و سایر عناصر برنامه. طبق کنوانسیون و استاندارد کدنویسی، هر آنوتیشن هنگام استفاده روی یک اعلان، معمولاً در یک خط مجزا قرار میگیرد.
از زمان انتشار Java SE 8 به بعد، آنوتیشنها را میتوان روی استفاده از نوعها (Type Uses) نیز اعمال کرد. در ادامه چند مثال آورده شده است:
عبارت ایجاد نمونه از کلاس (Class instance creation):
new @Interned MyObject();
تبدیل نوع (Type cast):
myString = (@NonNull String) str;
عبارت implements:
class UnmodifiableList<T> implements
@Readonly List<@Readonly T> { ... }
اعلان استثنای پرتابشده (Thrown exception):
void monitorTemperature() throws
@Critical TemperatureException { ... }
به این شکل از آنوتیشنها، آنوتیشنِ نوع (Type Annotation) گفته میشود.
بسیاری از آنوتیشنها جایگزین کامنتها (توضیحات) در کد میشوند.
فرض کنید یک تیم نرمافزاری بهطور سنتی بدنه هر کلاس را با کامنتهایی شروع میکند که اطلاعات مهمی را ارائه میدهند:
public class Generation3List extends Generation2List {
// Author: John Doe
// Date: 3/17/2002
// Current revision: 6
// Last modified: 4/12/2004
// By: Jane Doe
// Reviewers: Alice, Bill, Cindy
// class code goes here
}
برای افزودن همین متادیتا (ابرپاده/دادههای توصیفی) با استفاده از یک آنوتیشن، ابتدا باید «نوع آنوتیشن» را تعریف کنید. ساختار (سینتکس) انجام این کار به شکل زیر است:
@interface ClassPreamble {
String author();
String date();
int currentRevision() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// Note use of array
String[] reviewers();
}
تعریف نوع آنوتیشن شبیه به تعریف یک اینترفیس (Interface) است، با این تفاوت که قبل از کلمه کلیدی interface یک علامت آتساین (@) قرار میگیرد. نوعهای آنوتیشن در واقع شکلی از اینترفیسها هستند که در بخشهای بعدی به آنها پرداخته خواهد شد (در حال حاضر نیازی به درک کامل اینترفیسها ندارید).
بدنه تعریف آنوتیشن بالا شامل «اعلانِ المانهای نوع آنوتیشن» است که شباهت زیادی به متدها دارند. توجه داشته باشید که این المانها میتوانند مقادیر پیشفرض اختیاری (default) داشته باشند.
پس از تعریف نوع آنوتیشن، میتوانید از آنوتیشنهای آن نوع همراه با مقداردهی به المانهایش استفاده کنید، مانند این:
@ClassPreamble (
author = "John Doe",
date = "3/17/2002",
currentRevision = 6,
lastModified = "4/12/2004",
lastModifiedBy = "Jane Doe",
// Note array notation
reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {
// class code goes here
}
نکته: برای اینکه اطلاعاتِ موجود در
@ClassPreambleدر مستندات تولیدشده توسط Javadoc ظاهر شوند، باید تعریفِ خودِ@ClassPreambleرا با آنوتیشن@Documentedنشانه گذاری کنید:// import this to use @Documented import java.lang.annotation.*; @Documented @interface ClassPreamble { // Annotation element definitions }
مجموعهای از انواع آنوتیشنها در API اصلی Java SE بهصورت پیشفرض تعریف شدهاند. برخی از آنها توسط کامپایلر جاوا استفاده میشوند و برخی دیگر روی آنوتیشنهای دیگر اعمال میشوند.
آنوتیشنهای پیشفرضی که در پکیج java.lang تعریف شدهاند عبارتاند از: @Deprecated ،@Override و @SuppressWarnings.
@Deprecatedآنوتیشن @Deprecated نشان میدهد عنصرِ نشانه گذاریشده منسوخ (مطرود) شده است و دیگر نباید استفاده شود. هرگاه برنامهای از یک متد، کلاس یا فیلد دارای @Deprecated استفاده کند، کامپایلر یک هشدار (Warning) تولید میکند.
وقتی عنصری منسوخ میشود، باید در مستندات نیز با تگ deprecated@ در Javadoc ثبت شود (مطابق مثال زیر). استفاده از علامت آتساین (@) هم در کامنتهای Javadoc و هم در آنوتیشنها اتفاقی نیست و از نظر مفهومی به هم مرتبط هستند. همچنین توجه داشته باشید که تگ Javadoc با d کوچک و آنوتیشن با D بزرگ شروع میشود:
// Javadoc comment follows
/**
* @deprecated
* explanation of why it was deprecated
*/
@Deprecated
static void deprecatedMethod() { }
نکته: از نسخه Java SE 9 به بعد، سمپات/ویژگی
forRemovalبه آنوتیشن@Deprecatedاضافه شده است. این ویژگی مشخص میکند آیا عنصر مربوطه قرار است در نسخههای آینده بهطور کامل حذف شود یا خیر. مقدار پیشفرض آنfalseاست.
@Overrideآنوتیشن @Override به کامپایلر اطلاع میدهد که این عنصر قرار است متدی از کلاس والد (Superclass) را بازنویسی (Override) کند.
// mark method as a superclass method
// that has been overridden
@Override
int overriddenMethod() { }
اگرچه استفاده از این آنوتیشن هنگام بازنویسی متد اجباری نیست، اما به جلوگیری از خطاها کمک میکند. اگر متدی که با @Override نشانهگذاری شده، نتواند متدی را در کلاسهای والد خود بهدرستی بازنویسی کند، کامپایلر خطا (Error) میدهد.
@SuppressWarningsآنوتیشن @SuppressWarnings به کامپایلر میگوید هشدارهای خاصی را که در حالت عادی تولید میکرد، نادیده بگیرد (سرکوب کند). در مثال زیر، از یک متد منسوخشده استفاده شده که معمولاً هشدار ایجاد میکند، اما وجود این آنوتیشن جلوی نمایش هشدار را میگیرد:
// use a deprecated method and tell
// compiler not to generate a warning
@SuppressWarnings("deprecation")
void useDeprecatedMethod() {
// deprecation warning
// - suppressed
objectOne.deprecatedMethod();
}
هر هشدار کامپایلر به یک دسته تعلق دارد. مشخصات زبان جاوا (Java Language Specification) چهار دسته اصلی را معرفی میکند:
هشدارهای Unchecked (بررسینشده) با رشته "unchecked" مشخص میشوند.
هشدارهای Deprecation (منسوخی) با رشته "deprecation" مشخص میشوند.
هشدارهای Removal (حذف در آینده) با رشته "removal" مشخص میشوند.
هشدارهای Preview (ویژگیهای آزمایشی) با رشته "preview" مشخص میشوند.
هشدارهای unchecked معمولاً هنگام کار با کدهای قدیمی (نوشتهشده قبل از معرفی Generics) رخ میدهند. برای نادیده گرفتن چندین دسته از هشدارها بهطور همزمان، از ساختار زیر استفاده کنید:
@SuppressWarnings({"unchecked", "deprecation"})
@SafeVarargsآنوتیشن @SafeVarargs وقتی روی یک متد یا سازنده (Constructor) اعمال میشود، تضمین میکند که کد عملیات ناامنی روی پارامتر ورودیِ تعدادمتغیر (Varargs) انجام نمیدهد. با استفاده از این آنوتیشن، هشدارهای unchecked مربوط به ورودیهای varargs سرکوب میشوند.
@FunctionalInterfaceآنوتیشن @FunctionalInterface که در Java SE 8 معرفی شد، نشان میدهد اعلانِ این نوع (Type Declaration) قرار است یک اینترفیس تکمتدی (Functional Interface) باشد.
به آنوتیشنهایی که روی آنوتیشنهای دیگر اعمال میشوند، متا-آنوتیشن (Meta-Annotation) میگویند. چند نوع متا-آنوتیشن در پکیج java.lang.annotation تعریف شده است:
@Retentionآنوتیشن @Retention نحوه و طولعمر ذخیرهسازی آنوتیشنِ نشانهگذاریشده را تعیین میکند:
RetentionPolicy.SOURCE – آنوتیشن فقط در سطح سورسکد باقی میماند و کامپایلر آن را نادیده میگیرد.
RetentionPolicy.CLASS – آنوتیشن در زمان کامپایل حفظ میشود، اما توسط ماشین مجازی جاوا (JVM) در زمان اجرا نادیده گرفته میشود.
RetentionPolicy.RUNTIME – آنوتیشن توسط JVM حفظ شده و در محیط زمان اجرا (Runtime) قابل بررسی و دسترسی است.
@Documentedآنوتیشن @Documented نشان میدهد هرگاه از آنوتیشن مربوطه استفاده شد، آن عناصر باید توسط ابزار Javadoc مستندسازی شوند (بهطور پیشفرض، آنوتیشنها در Javadoc قرار نمیگیرند).
@Targetآنوتیشن @Target محدودیتهایی روی عناصر قابلپشتیبانی اعمال میکند تا مشخص شود آنوتیشن روی چه نوع عناصر جاوایی قابل استفاده است. مقادیر آن از نوع ElementType است:
ElementType.ANNOTATION_TYPE – قابل اعمال روی خودِ انواع آنوتیشن.
ElementType.CONSTRUCTOR – قابل اعمال روی سازندهها.
ElementType.FIELD – قابل اعمال روی فیلدها یا ویژگیها (Properties).
ElementType.LOCAL_VARIABLE – قابل اعمال روی متغیرهای محلی.
ElementType.METHOD – قابل اعمال روی متدها.
ElementType.MODULE – قابل اعمال روی اعلان ماژولها.
ElementType.PACKAGE – قابل اعمال روی اعلان پکیجها.
ElementType.PARAMETER – قابل اعمال روی پارامترهای متد.
ElementType.RECORD_COMPONENT – قابل اعمال روی مؤلفههای ریکورد (Record).
ElementType.TYPE – قابل اعمال روی اعلان کلاس، کلاس انتزاعی، اینترفیس، آنوتیشن، Enum یا Record.
ElementType.TYPE_PARAMETER – قابل اعمال روی پارامترهای نوع (Type Parameters).
ElementType.TYPE_USE – قابل اعمال در هر جایی که یک «نوع» استفاده میشود (مثلاً هنگام تعریف فیلد).
@Inheritedآنوتیشن @Inherited نشان میدهد که نوع آنوتیشن میتواند از کلاس والد به ارث برده شود (این رفتار به صورت پیشفرض وجود ندارد). اگر کلاسی این آنوتیشن را نداشته باشد و از آن استعلام شود، جاوا کلاس والد آن را بررسی میکند. این آنوتیشن فقط روی اعلان کلاسها کاربرد دارد.
@Repeatableآنوتیشن @Repeatable که در Java SE 8 معرفی شد، نشان میدهد آنوتیشن مربوطه میتواند بیشتر از یک بار روی همان اعلان یا استفاده از نوع (Type Use) اعمال شود.
قبل از انتشار Java SE 8، آنوتیشنها فقط بر روی اعلانها (Declarations) قابل اعمال بودند. اما از نسخه Java SE 8 به بعد، آنوتیشنها را میتوان روی هرگونه استفاده از «نوع» (Type Use) نیز اعمال کرد. این بدین معناست که هر کجا از یک نوع داده استفاده کنید، میتوانید آنوتیشن نیز بهکار ببرید.
چند نمونه از جاهایی که نوعها استفاده میشوند عبارتاند از:
عبارات ایجاد نمونه از کلاس (new)
تبدیل نوعها (Casting)
عبارتهای implements
عبارتهای throws
به این شکل از آنوتیشن، آنوتیشنِ نوع (Type Annotation) گفته میشود که پیشتر چند نمونه از آن در بخش «موارد استفاده از آنوتیشنها» آورده شده بود.
آنوتیشنهای نوع برای پشتیبانی از تحلیل بهتر برنامههای جاوا و راهکاری برای تضمین بررسی دقیقتر نوعها (Stronger Type Checking) ایجاد شدند. نسخه Java SE 8 خود به تنهایی یک فریمورکِ بررسی نوع (Type-checking Framework) ارائه نمیدهد، اما این امکان را فراهم میکند تا فریمورکی برای بررسی نوع بنویسید (یا دانلود کنید) که به صورت یک یا چند ماژول قابلافزودن (Pluggable Modules) در کنار کامپایلر جاوا استفاده میشود.
برای مثال، فرض کنید میخواهید مطمئن شوید متغیر خاصی در برنامه شما هرگز مقدار null نمیگیرد تا از بروز خطای NullPointerException جلوگیری کنید. میتوانید یک پلاگین سفارشی بنویسید تا این موضوع را بررسی کند. سپس کد خود را تغییر میدهید تا با آنوتیشنگذاری روی آن متغیر مشخص، نشان دهید که هرگز null نخواهد شد. اعلان متغیر ممکن است به این شکل باشد:
@NonNull String str;
هنگامی که کد را کامپایل میکنید (با افزودن ماژول NonNull در خط فرمان)، اگر کامپایلر مشکل محتمل و پتانسیل خطایی را تشخیص دهد، یک هشدار چاپ میکند و به شما این امکان را میدهد که کد را برای جلوگیری از خطا اصلاح کنید. پس از اصلاح کد و برطرف کردن همه هشدارها، این خطای خاص هنگام اجرای برنامه رخ نخواهد داد.
شما میتوانید از چندین ماژول بررسی نوع استفاده کنید که هر ماژول نوع متفاوتی از خطا را بررسی میکند. به این ترتیب، میتوانید قابلیتهای جدیدی روی سیستم نوع جاوا بسازید و بررسیهای خاصی را در زمان و مکان دلخواه به آن اضافه کنید.
با استفاده هوشمندانه از آنوتیشنهای نوع و حضور بررسیکنندههای قابلافزودن (Pluggable Type Checkers)، میتوانید کدی قویتر و با خطای کمتر بنویسید.
در بسیاری از موارد، نیازی نیست خودتان ماژولهای بررسی نوع را بنویسید، چرا که دیگران این کار را انجام دادهاند. برای مثال، میتوانید از Checker Framework که توسط دانشگاه واشنگتن توسعه یافته استفاده کنید. این فریمورک شامل ماژول NonNull و همچنین ماژولهای عبارتهای باقاعده (Regex) و قفلهای همگامسازی (Mutex Lock) است. برای اطلاعات بیشتر، میتوانید به مستندات Checker Framework مراجعه کنید.
در برخی شرایط ممکن است بخواهید یک آنوتیشن یکسان را چند بار روی یک اعلان یا استفاده از یک نوع (Type Use) اعمال کنید. از زمان انتشار Java SE 8 به بعد، آنوتیشنهای تکرارشونده این امکان را برای شما فراهم کردهاند.
برای مثال، فرض کنید در حال نوشتن کدی برای استفاده از یک سرویس تایمر (مشابه سرویس cron در سیستمعامل UNIX) هستید که به شما امکان میدهد متدی را در یک زمان مشخص یا طبق یک برنامه زمانی خاص اجرا کنید. حالا میخواهید تایمری تنظیم کنید تا متد ()doPeriodicCleanup را در آخرین روز ماه و همچنین هر جمعه ساعت ۱۱ شب اجرا کند. برای این کار، یک آنوتیشن @Schedule میسازید و آن را دو بار روی متد ()doPeriodicCleanup اعمال میکنید؛ بار اول برای آخرین روز ماه و بار دوم برای جمعه ساعت ۱۱ شب، مطابق کد زیر:
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }
مثال بالا یک آنوتیشن را روی یک متد تکرار کرده است. شما میتوانید یک آنوتیشن را در هر جایی که امکان استفاده از آنوتیشنهای استاندارد وجود دارد، تکرار کنید. به عنوان مثالی دیگر، فرض کنید کلاسی برای مدیریت استثناهای دسترسی غیرمجاز (Unauthorized Access) دارید. میتوانید این کلاس را با یک آنوتیشن @Alert برای مدیران (Managers) و یک آنوتیشن دیگر برای ادمینها (Admins) نشانه گذاری کنید:
@Alert(role="Manager")
@Alert(role="Administrator")
public class UnauthorizedAccessException extends SecurityException { ... }
به دلیل حفظ سازگاری (Compatibility)، آنوتیشنهای تکرارشونده در یک آنوتیشن کانتینر (حاوی/شاملکننده) نگهداری میشوند که به صورت خودکار توسط کامپایلر جاوا تولید میشود. برای اینکه کامپایلر بتواند این کار را انجام دهد، به دو مرحله اعلان در کد شما نیاز است:
نوع آنوتیشن باید با متا-آنوتیشن @Repeatable نشانهگذاری شود. مثال زیر یک آنوتیشن سفارشی تکرارشونده به نام @Schedule را تعریف میکند:
import java.lang.annotation.Repeatable;
@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}
مقدار داخل پرانتزِ متا-آنوتیشن @Repeatable، نوعِ همان «آنوتیشن کانتینری» است که کامپایلر جاوا برای ذخیره آنوتیشنهای تکراری تولید میکند. در این مثال، نوع آنوتیشن کانتینر @Schedules است؛ بنابراین آنوتیشنهای تکراریِ @Schedule در یک آنوتیشن @Schedules ذخیره میشوند.
نکته: اعمال مجدد یک آنوتیشن روی یک اعلان، بدون اینکه ابتدا آن را به صورت
@Repeatableتعریف کرده باشید، منجر به خطای زمان کامپایل (Compile-time Error) خواهد شد.
نوع آنوتیشن کانتینر باید دارای یک المان به نام value از نوع آرایه باشد. نوع درایههای این آرایه باید همان نوع آنوتیشن تکرارشونده باشد. اعلان آنوتیشن کانتینرِ @Schedules به شکل زیر است:
public @interface Schedules {
Schedule[] value();
}
متدهای مختلفی در Reflection API برای بازیابی آنوتیشنها وجود دارند:
متدهایی که یک آنوتیشن واحد را برمیگردانند (مانند AnnotatedElement.getAnnotation(Class)) تغییر نکردهاند و اگر تنها یک آنوتیشن از نوع درخواستی وجود داشته باشد، همان را برمیگردانند. اما اگر بیش از یک آنوتیشن از آن نوع وجود داشته باشد، ابتدا باید آنوتیشن کانتینر آنها را دریافت کنید. این کار باعث میشود کدهای قدیمی (Legacy Code) همچنان بدون مشکل کار کنند.
متدهای جدیدتری در Java SE 8 معرفی شدند که تمام آنوتیشنهای درون کانتینر را اسکن کرده و آنها را یکجا برمیگردانند؛ مانند AnnotatedElement.getAnnotationsByType(Class). برای مشاهده تمام متدهای موجود، میتوانید به مستندات کلاس AnnotatedElement مراجعه کنید.
هنگام طراحی یک نوع آنوتیشن، باید تعداد دفعات مجاز استفاده (Cardinality) از آن را مد نظر قرار دهید. اکنون این امکان وجود دارد که از یک آنوتیشن صفر بار، یک بار، یا در صورتی که به صورت @Repeatable علامتگذاری شده باشد، چند بار استفاده کنید.
همچنین میتوان با استفاده از متا-آنوتیشن @Target محدود کرد که آنوتیشن در چه مکانهایی قابل استفاده باشد (مثلاً ساخت آنوتیشن تکرارشوندهای که فقط روی متدها و فیلدها کاربرد داشته باشد). بسیار مهم است که آنوتیشن خود را با دقت طراحی کنید تا مطمئن شوید برنامهنویسان هنگام استفاده از آن بیشترین انعطافپذیری و قدرت را در اختیار دارند.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/