, جاوا انوتیشن اطلاعات اضافی به کد ما اضافه می کند که بخشی از برنامه نیست ولی تاثیر در اجرای برنامه می گذارد.
موضاعات مهمی که در انوتیشن مطرح است عبارتند از اطلاع رسانی به کامپایلر، انجام پردازش در زمان کامپایل و همچنین پردازش در زمان اجرا می باشد. انوتیشن ها توسط کامپایلر و یا ابزارهای تجزیه انوتیشن، تجزیه می شوند. قبل از این اطلاعات اضافی در javadoc قرار می گرفت، ولی توسط انوتیشن این اطلاعات را می توان در زمان کامپایل و حتی در زمان اجرا داشته باشیم. چند مثالی کاربردی خواهیم داشت و امیدوارم تا آخر این آموزش با من همراه باشید.
انوتیشن های زیادی توسط خود زبان جاوا استفاده می شوند که شاید با خیلی از آنها همچون @Override آشنا باشید. علاوه بر انواع موجود می توان انوتیشن اختصاصی داشته باشیم که در مثال های بعدی با آنها مواجه خواهیم شد. توجه داشته باشید که انوتیشن می تواند ورودی داشته باشد یا اینکه نداشته باشد.
چند نمونه:
@Override به کامپایلر می گوید که متد اظهار شده در کلاس اصلی را با دستوراتی ما بازنویسی کند.
@SuppressWarnings می گوید که اخطارها را سرکوب کن. گرچه نمایش اخطار مفید است ممکن است در مواردی بخواهیم تا کامپایلر اخطاری را نمایش ندهد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @SuppressWarnings("unused") public void unusedmethod(){ //some methods } @SuppressWarnings("deprecation") public void oldmethdthatDepricated(){ //some methods } |
بعضی انوتیشن ها هم برای توصیف انوتیشن های دیگر بکار می رود. یعنی انوتیشن برای انوتیشن:
@Retention می گوید که انوتیشن موردنظر چگونه ذخیره شود. استفاده است مثلا RetentionPolicy.SOURCE
انوتیشن اشاره شده در سطح سورس کدمی ماند و توسط کامپایلر قابل دسترسی نباشد. اگر مقدار را RetentionPolicy.CLASS
بگذاریم باعث می شود که انوتیشن مان در زمان کامپایل در نظر گرفته شود و توسط JVM ایگنور شود و یا توسط RetentionPolicy.RUNTIME
انوتیشنمان توسط JVM نگهداری شود و در زمان اجرا قابل استفاده است.
@Documented می گوید که انوتیشن بکار رفته باید توسط ابزار سندسازی جاوا به سند تبدیل شوند.
@Target توسط این انوتیشن، دسترسی المنت های جاوا به انوتیشن ما را محدود می کند و انواع انتخاب شده می توانند به انوتیشن ما دسترسی داشته باشند.
@Inherited توجه کنید که انوتیشن ها ارث بری ندارند. یعنی مثلا اگر یک کلاسی با یک انوتیشن خاصی درست کردید و یک کلاس دیگر از آن مشتق شد اگر از این انوتیشن استفاده نکرده باشید کلاس جدید انوتیشن های پدر را به ارث نمی برد. در مثالی که بعد از تعاریف می زنم تمام این مطالب را نمایش خواهم داد.
@Repeatable این امکان در جاوای ۸ اضافه شده که می توانیم چند انوتیشن یکسان ولی با مقادیر مختلف را به یک متد اضافه کنیم. مثلاً در مثال زیر دوبار انوتیشن آلارم را با دو role متفاوت اعمال کردیم.
1 2 3 |
@Alert(role="Manager") @Alert(role="Administrator") public class UnauthorizedAccessException extends SecurityException { ... } |
حال که با برخی انوتیشن های پیش فرض آشنا شدیم می خواهیم نحوه ساخت انوتیشن را باهم برسی کنیم. انوتیشن بسیار شبیه به اینترفیس ساخته می شود با این تفاوت که از کاراکتر @ استفاده می کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.example.mtajik.annotationtest; import java.lang.annotation.*; @Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface BasicInformation { String corpId() default "mobiro"; String corpAddress() default "kordestan .st"; } |
انوتیشنی ساختیم که اطلاعات کلی کارمندان را نگهداری می کند. همانطور که ملاحظه می کنید انوتیشن می تواند متد داشته باشد. متد های آن می تواند مقدار پیش فرض داشته باشد. همچنین انوتیشن ما خودش چند انوتیشن دارد که در بالا تعریف کردیم. سپس کلاس کارمند با انوتیشن تولید شده را بصورت زیر می سازیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.example.mtajik.annotationtest; @BasicInformation(corpId="Irancell", corpAddress="Vanak .ave") public class Employee { String eId; String eName; public Employee(String eId, String eName){ this.eId = eId; this.eName = eName; } public void getEmployeeDetails(){ System.out.println("Employee ID: "+eId); System.out.println("Employee Name: "+eName); } } |
سپس کلاس زیر را بسازید و برنامه را اجرا کرده و نتیجه را در کنسول ببینید. همانطور که در اول اشاره کردم این کلاس همان ابزار تجزیه انوتیشن است که توسط reflection در هنگام Runtime انوتیشن های موجود در کلاس را گرفته و نتیجه را نمایش می دهد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.example.mtajik.annotationtest; import java.lang.annotation.Annotation; public class TestCustomAnnotationBasicBenefits { public static void main(String[] args) throws Exception{ Employee emp = new Employee("E-100", "user3320018"); emp.getEmployeeDetails(); Class reflectedClass = emp.getClass(); Annotation informationAnn = reflectedClass.getAnnotation(BasicInformation.class); BasicInformation bBenefits = (BasicInformation)informationAnn; System.out.println("corporation ID: "+bBenefits.corpId()); System.out.println("corporation address: "+bBenefits.corpAddress()); } } |
ملاحظه کردید که چگونه می توان با استفاده از Annotation از Meta-data در هنگام Runtime دسترسی پیدا کرد و استفاده کرد.
همچنین می توان با استفاده از انوتیشن نوع ورودی را برای یک تابع محدود کرد و با آن مانند enum برخورد کرد. هرچند به نظر شخصی بنده بسیار مفید تر از enum خواهد بود زیرا اطلاعاتی به همراه دارد که بسیار جای مانور دارد. به مثال زیر توجه کنید.
IntDef
انوتیشنی است که توسط آن یک عنصر integer را ارائه می دهد که در هنگام استفاده فقط می توانیم از مقادیر داده شده استفاده کنیم. ما یک انوتیشن داریم به عنوان روزهای هفته که انواع آن را از مقادیر static انتخاب کردیم. ( انواع آن روزهای هفته است)
1 2 3 4 5 6 7 |
static public final int SHANBE_ = 0; static public final int YEK_SHANBE_ = 1; static public final int DO_SHANBE = 2; static public final int SE_SHANBE_ = 3; static public final int CHAHR_SHANBE_ = 4; static public final int PANJ_SHANBE_ = 5; static public final int JOMHE_ = 6; |
1 2 3 4 |
@IntDef({SHANBE_, YEK_SHANBE_,DO_SHANBE,SE_SHANBE_,CHAHR_SHANBE_,PANJ_SHANBE_, JOMHE_}) @Retention(RetentionPolicy.SOURCE) @Inherited public @interface RozayeHafte {} |
حال متدی برای محاسبه حقوق داریم که ورودی آن را از جنس این انوتیشن می گذاریم. حال هر جا که متد را صدا بزنید intellisense ide یعنی Android Studio به عوان ورودی پیش فرض همان روز ها را به ما پیشنهاد می دهد! جالب است نه؟ این همان کاری است که در هنگام انتخاب visibility در View خودش بطور پیش فرض GONE و Visible و Invisible را پیشنهاد می دهد و اگر سورس کد View را در SDK های زیر ۲۲ ببینید ملاحظه خواهید کرد که از همین روش برای مشخص کردن ورودی هایش استفاده کرده است.
استفاده از StringDef هم بهمین صورت است با این تفاوت که انواع آن string هستند.
سورس کامل
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
public class MainActivity extends AppCompatActivity { static public final int SHANBE_ = 0; static public final int YEK_SHANBE_ = 1; static public final int DO_SHANBE = 2; static public final int SE_SHANBE_ = 3; static public final int CHAHR_SHANBE_ = 4; static public final int PANJ_SHANBE_ = 5; static public final int JOMHE_ = 6; static public final String GHAWY = "ghavi"; static public final String ZAEEF = "zaeef"; static public final String PIZORI = "pizoori"; public void mohasebeheHoggogh(@RozayeHafte int day, @GhodratBadani String body) { switch (day) { case SHANBE_: Toast.makeText(MainActivity.this, body + " SHANBE_", Toast.LENGTH_SHORT).show(); break; case YEK_SHANBE_: Toast.makeText(MainActivity.this, body + " YEK_SHANBE_", Toast.LENGTH_SHORT).show(); break; case JOMHE_: Toast.makeText(MainActivity.this, body + " JOMHE_", Toast.LENGTH_SHORT).show(); break; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mohasebeheHoggogh(JOMHE_, GHAWY); } private enum RozhayeHafteType2 { SHANBE_, YEK_SHANBE_, JOMHE_ } @IntDef({SHANBE_, YEK_SHANBE_, DO_SHANBE, SE_SHANBE_, CHAHR_SHANBE_, PANJ_SHANBE_, JOMHE_}) @Retention(RetentionPolicy.SOURCE) @Inherited public @interface RozayeHafte { } @StringDef({GHAWY, ZAEEF, PIZORI}) @Inherited @Retention(RetentionPolicy.SOURCE) public @interface GhodratBadani { } } |
منبع: بایت کد