آموزش جاوا SEبا -intellij (3) - Annotation

Annotation یکی از مفاهیمی است که در جاوا 1.5 معرفی شد، روشی برای مستندسازی اجزای برنامه از قبیل کلاسهای، اینترفیس ها، متدها، فیلدها، پارامترهای متدها، و متغیرهای محلی است.
يکشنبه، 7 ارديبهشت 1393
تخمین زمان مطالعه:
موارد بیشتر برای شما
آموزش جاوا SEبا -intellij (3) - Annotation
آموزش جاوا    SEبا -intellij  (3) - Annotation

 

مترجم: حبیب الله علیخانی
منبع:راسخون




 

Annotation (ضمیمه، یادداشت)

Annotation یکی از مفاهیمی است که در جاوا 1.5 معرفی شد، روشی برای مستندسازی اجزای برنامه از قبیل کلاسهای، اینترفیس ها، متدها، فیلدها، پارامترهای متدها، و متغیرهای محلی است.
Annotation ضمیمه هایی است که شما برای پردازش بوسیله ی ابزارها، به سورس کدتان اضافه می کنید. Annotation ها می توانند در سطح کد پردازش شوند یا کامپایلر می تواند آنها را به صورت فایلهای کلاس دربرداشته باشد.
Annotation ها مسیر را در برنامه ی کامپایل شده تغییر نمی دهند. (کامپایلر JDK 5.0 یک جفت Annotation برای تولید یا متوقف کردن هشدارها استفاده می کند،اما فایل های کلاس مشابه دارای Annotation یا بدون Annotation تولید می کند. این Annotation ها ساده و تاحدى غیرمعمول هستند زیرا شخص سوم دیگر، نمی تواند Annotation جدیدی به کامپایلر اضافه کند)
نیاز دارید ابزار پردازشی را انتخاب کنید، و Annotation را به سورس کدتان وارد کنید تا آن ابزار پردازش آنرا دریافت کند و سپس بکار ببرد.
ممکن است در جاهای زیادی از Annotation استفاده کنید مانند:
• تولید اتوماتیک فایلهای کمکی، مانند گسترش توصیفگر یا کلاسهای بینز
• تولید اتوماتیک کد برای آزمایش، ثبت وقایع (logging)، تراکنش معنایی و غیره
در این بخش ما می خواهیم با مفاهیم ابتدایی و استفاده ی آنها در مثال ها شروع کنیم. ما متدها را به عنوان لیستنر رویدادها برای کامپوننت AWT نشانه گذاری می کنیم و یک پردازشگر annotation که آنها را آنالیز می کند و لیستنر را فعال می کند، را نشان خواهیم داد.
Annotation نه یک کلاس است و نه یک اینترفیس.
اضافه کردن متادیتا به برنامه
متادیتا داده هایی درباره ی داده می باشد. در مفاهیم برنامه نویسی کامپیوتر، متادیتا داده های درباره ی کد می باشد. یک مثال خوب برای متادیتا کامنت های Javadoc می باشد. کامنت کد را توصیف می کند اما آنها کار کد را تغییر نمی دهند.
در JDK 5.0 می توانید برای وارد کردن داده های دلخواه به سورس کد، از annotation استفاده کنید. در زیر نمونه ای از annotation ساده مشاهده می کنید:
public class MyClass
{
. . .
@TestCase public void checkRandomInsertions()
}
نام annotation بالا TestCase می باشد که متد checkRandomInsertions را توصیف و تفسیر می کند.
یک annotation در جاوا مانند یک modifier استفاده شده و قبل از آیتم توصیف کننده بدون سمی کالن قرار می گیرد. (modifier کلمه ی کلیدی مانند public یا static است). قبل از نام هر annotation، علامت @ می آید.
کامنت های Javadoc درون /** . . . */ قرار می گیرد ، اکنون ما می خواهیم طبق یک استانداردی کامنت گذاری کنیم البته کاربرد گسترده ای بجز کامنت گذاری دارد. بادر نظر گرفتن اینکه annotation بخشی از کد می باشند. @TestCase کاری را انجام نمی دهد و به ابزاری نیاز دارد تا مفید واقع شود. برای مثال وقتی یک کلاس را تست می کنیم، یک ابزار تست همه ی متد هایی که با @TestCase برچسب خورده، را ممکن است فراخوانی کند. ابزار دیگری ممکن است همه ی متدهای تست را از فایل کلاس حذف کند بطوریکه بعد از اینکه تست شده، دیگر همراه برنامه حمل نشود.
JUnit که در سایت http://junit.org در دسترس است، یک ابزار تست مشهور است که برای شناسایی متد هایی که در طول تست فراخوانی می شود، ازقرارداد نامگذاری استفاده می کند. نام متدها باید با پیشوند test شروع شود. مقاله ی http://www.langrsoft.com/articles/annotations.html چگونگی تشخیص JUnit برای استفاده از annotation را توصیف می کند.
Annotation می تواند طوری تعریف شود که اجزایی داشته باشد، مانند:
@TestCase(id="3352627")
این اجزا می توانند با ابزاری که annotationها را می خوانند، پردازش شوند.
علاوه بر متد ها شما می توانید کلاسها ، فیلد ها را هم تفسیر یا annotate کنید. Annotation متغییر های محلی می تواند هرجایی modifier که را قرار می دهید، قرار گیرد. مانند public یا static.
هر Annotation باید با یک اینترفیس Annotation تعریف شود. متد های اینترفیس مشابه اجزای Annotation می باشد.
برای مثال، یک Annotation ، TestCase می تواند بوسیله ی اینترفیس زیر تعریف شود:
public class annotation {
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestCase
{
String id() default "[none]";
}
}
اعلان @interface یک اینترفیس واقعی جاوا را ایجاد می کند. ابزارهایی که annotationها را پردازش می کند آبجت ها را دریافت می کند که اینترفیس annotation را implement می کنند. یک ابزار ممکن است متد id را برای بازیابی عنصر id یک annotation، TestCase بخصوص فراخوانی کند.
Annotation های Target و Retention، meta-annotations هستند. آنها TestCase را توصیف می کنند. علامتگذاری آن به عنوان یک Annotation می تواند فقط برای متدها عملی باشد و وقتی فایل کلاس به ماشین مجازی لود می شود، حفظ می شود.
یک مثال : annotate کردن یک event handler
یکی از وظایف خسته کننده ی برنامه نویسی یوزر اینترفیس اتصال لیستنر ها به منابع رویداد می باشد. تعدادی از لیستنر ها برای فرم هستند.
public class annotation {
myButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
doSomething();
}
});
}
در این بخش ما یک annotate طراحی کردیم که از این زحمات جلوگیری می کند.
@ActionListenerFor(source="myButton") void doSomething() { . . . }
برنامه نویس مجبور نیست فراخوانی طولانی addActionListener را داشته باشد ، در عوض هر متد با annotation علامتگذاری شده. در مثال زیر کلاس ButtonTest را در نظر بگیرید که شامل متد main می باشد:
1
2 package annotate;
3
4
5 import java.awt.Color;
6 import javax.swing.JButton;
7 import javax.swing.JFrame;
8 import javax.swing.JPanel;
9
10 public class ButtonTest
11 {
12 public static void main(String[] args)
13 {
14 ButtonFrame frame = new ButtonFrame();
15 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
16 frame.setVisible(true);
17 }
18 }
19
20 /**
21 A frame with a button panel
22 */
23 class ButtonFrame extends JFrame
24 {
25 public ButtonFrame()
26 {
27 setTitle("ButtonTest");
28 setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
29
30 panel = new JPanel();
31 add(panel);
32
33 // create buttons
34
35 yellowButton = new JButton("Yellow");
36 blueButton = new JButton("Blue");
37 redButton = new JButton("Red");
38
39 // add buttons to panel
40
41 panel.add(yellowButton);
42 panel.add(blueButton);
43 panel.add(redButton);
44
45 ActionListenerInstaller.processAnnotations(this);
46 }
47
48
49 @ActionListenerFor(source="yellowButton")
50 public void yellowBackground()
51 {
52 panel.setBackground(Color.YELLOW);
53 }
54
55 @ActionListenerFor(source="blueButton")
56 public void blueBackground()
57 {
58 panel.setBackground(Color.BLUE);
59 }
60
61 @ActionListenerFor(source="redButton")
62 public void redBackground()
63 {
64 panel.setBackground(Color.RED);
65 }
66
67 public static final int DEFAULT_WIDTH = 300;
68 public static final int DEFAULT_HEIGHT = 200;
69
70 private JPanel panel;
71 private JButton yellowButton;
72 private JButton blueButton;
73 private JButton redButton;
74 }
75
و خروجی به صورت زیر می باشد:
آموزش جاوا    SEبا -intellij  (3) - Annotation
ما همچنین نیاز داریم که یک اینترفیس annotation داشته باشیم. که کد آن در زیر آمده است:
1
2 package annotate;
3 import java.lang.annotation.ElementType;
4 import java.lang.annotation.Retention;
5 import java.lang.annotation.RetentionPolicy;
6 import java.lang.annotation.Target;
7
8 @Target(ElementType.METHOD)
9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface ActionListenerFor
11 {
12 String source();
13 }
14
البته annotation ها خودشان ماری را انجام نمی دهند. آنها در فایل سورس قرارمی گیرند. کامپایلر آنها را در فایل کلاس قرار می دهند و ماشین مجازی آنها را لود می کند. ما اکنون به مکانیزمی برای آنالیز آنها نیاز داریم و action listenerها را قرار دهیم. و این کار کلاس ActionListenerInstaller می باشد(کد های آن جلوتر آمده است) که سازنده ی ButtonFrame را صدا می زند.
ActionListenerInstaller.processAnnotations(this);
متد استاتیک processAnnotations همه ی متدهای آبجکتی که دریافت کرد را در نظر می گیرد. برای هر متد، آبجکتی از annotation، ActionListenerFor را می گیرد و آن را پردازش می کند.
Class cl = obj.getClass();
for (Method m : cl.getDeclaredMethods())
{
ActionListenerFor a = m.getAnnotation(ActionListenerFor.class);
if (a != null) . . .
}
در اینجا از متد getAnnotation استفاده می کنیم که در اینترفیس AnnotatedElement تعریف می شود و کلاسهای
Method, Constructor, Field, Class و Package این اینترفیس را implement می کنند.
نام فیلد سورس در آبجکت annotation ذخیره می شود. ما آن را با فراخوانی متدsource() بازیابی می کنیم و سپس آن را با تطابق پیدا می کنیم:
String fieldName = a.source();
Field f = cl.getDeclaredField(fieldName);
این یک محدودیت از annotation را نشان می دهد. عنصر source باید نام یک فیلد باشد و نمی تواند متغییر محلی باشد. بقیه ی کد نسبتا تکنیکی است. برای هر متد annotate شده، ما یک آبجکت پروکسی(نماینده) می سازیم که اینترفیس ActionListener را implement می کند و متد actionPerformed مربوط به آن متد annotate شده را فراخوانی می کند.نکته مهم این است که اصول annotation ها بوسیله متد processAnnotations محقق شده.
در این مثال، annotation در زمان اجرا پردازش شده است. این امکان وجود داشت که در سطح سورس آنرا پردازش کند. یک تولید کننده ی سورس کد، ممکن است کدی را برای اضافه کردن لیستنرها تولید کند. متناوبا، annotationها ممکن است در سطح بایت کد پردازش شوند.
1
2 package annotate;
3
4 import java.awt.event.ActionListener;
5 import java.lang.reflect.*;
6
7 public class ActionListenerInstaller
8 {
9 /**
10 Processes all ActionListenerFor annotations in the given object.
11 @param obj an object whose methods may have ActionListenerFor annotations
12 */
13 public static void processAnnotations(Object obj)
14 {
15 try
16 {
17 Class cl = obj.getClass();
18 for (Method m : cl.getDeclaredMethods())
19 {
20 ActionListenerFor a = m.getAnnotation(ActionListenerFor.class);
21 if (a != null)
22 {
23 Field f = cl.getDeclaredField(a.source());
24 f.setAccessible(true);
25 addListener(f.get(obj), obj, m);
26 }
27 }
28 }
29 catch (Exception e)
30 {
31 e.printStackTrace();
32 }
33 }
34
35 /**
36 Adds an action listener that calls a given method.
37 @param source the event source to which an action listener is added
38 @param param the implicit parameter of the method that the listener calls
39 @param m the method that the listener calls
40 */
41 public static void addListener(Object source, final Object param, final Method m)
42 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
43 {
44 InvocationHandler handler = new
45 InvocationHandler()
46 {
47 public Object invoke(Object proxy, Method mm, Object[] args) throws Throwable
48 {
49 return m.invoke(param);
50 }
51 };
52
53 Object listener = Proxy.newProxyInstance(null,
54 new Class[] { java.awt.event.ActionListener.class },
55 handler);
56 Method adder = source.getClass().getMethod("addActionListener", ActionListener
57 .class);
58 adder.invoke(source, listener);
59 }
60 }
java.lang.AnnotatedElement 5.0
• boolean isAnnotationPresent(Class<? extends Annotation> annotationType)
اگراین آیتم یک annotation نوع (نوع گرفته شده) داشته باشد، true برمی گردند
• <T extends Annotation> T getAnnotation(Class<T> annotationType)
annotation نوع را می گیرد، یا اگر این آیتم هیچ annotation ای نداشته باشد null می پذیرد.
• Annotation[] getAnnotations()
• Annotation[] getDeclaredAnnotations()
همه ی هایی که برای این آیتم ارائه شده را می گیرد، annotation های به ارث برده را هم شامل می شود. اگر هیچ annotation ای ارائه نشود، یک آرایه به طول صفر را برمی‌گرداند.
• Annotation[] getDeclaredAnnotations()
همه‌ی annotation هایی که برای این آیتم اعلان(Declared) شده را بر‌می‌گرداند، annotation های به ارث برده را شامل نمی شود. اگر هیچ annotation ای ارائه نشود، یک آرایه به طول صفر را برمی‌گرداند.

سینتکس(ساختار) annotation

یک annotation به وسیله‌ی یک اینترفیس تعریف می‌شود:
modifiers @interface annotation نام
{
element declaration1
element declaration2
. . .
}
هر element declaration به شکل زیر است:
type elementName();
یا
type elementName() default value;
برای مثال، annotation زیر دو عنصر(element) دارد، assignedTo و severity .
public @interface BugReport
{
String assignedTo() default "[none]";
int severity() = 0;
}
هر annotationفرمت زیر را دارد:
@AnnotationName(elementName1=value1, elementName2=value2, . . .)
برای مثال:
@BugReport(assignedTo="Harry", severity=10)
ترتیب عناصر مهم نیست.
@BugReport(severity=10, assignedTo="Harry")
اگربرای یک عنصرمقداری درنظر گرفته نشود، مقدار پیشفرض را استفاده می‌کند. برای مثال annotation زیر را درنظر بگیرید:
@BugReport(severity=10)
مقدارعنصر assignedTo رشته ی "[none]" است.
دو میانبر خاص annotation ها را آسانتر می کند.
اگر هیچ عنصری تعیین نشود، یا بدلیل اینکه annotation هیچ مقدار پیشفرضی نداشته باشد و یا بدلیل اینکه همه ی آنها از یک مقدار پیشفرض استفاده کرده باشند،پس نیاز ندارید از پرانتزها استفاده کنید. برای مثال:
@BugReport
با کد زیر یکسان است:
@BugReport(assignedTo="[none]", severity=0)
یک چنین annotation ای marker annotation نامیده می شود.
ساختار دیگر annotation تک مقداری می باشد. اگر یک عنصر با نام خاص value داشته باشیم و عنصر دیگری نداشته باشیم، شما می توانید نام آن و علامت = را حذف کنید . برای مثال ما اینترفیس annotation بخش قبلی را با نام ActionListenerFor تعریف کردیم:
public @interface ActionListenerFor
{
String value();
}
سپس می توانیم کد زیر را:
@ActionListenerFor("yellowButton")
بجای کد زیر بنویسیم:
@ActionListenerFor(value="yellowButton")
همه ی اینترفیس annotation ها به طور ضمنی از اینترفیس java.lang.annotation.Annotation بسط می یابند. این اینترفیس یک اینترفیس عمومی است و یک اینترفیس annotation نیست.
شما نمی توانید اینترفیس annotation را بسط دهید. به عبارتی دیگر همه ی اینترفیس annotationها به طور مستقیم، java.lang.annotation.Annotation. را بسط می دهند.
نوع هر عنصر annotation به صورت یکی از موارد زیر می باشد:

1- یک نوع اولیه(int, short, long, byte, char, double, float, boolean)
2- یک رشته یا string
3- کلاس یا class(با نوع دلخواه انتخابی مانندClass<? extends MyClass>)
4- یک نوع enum
5- یک نوع annotation
6- آرایه ای از انواع قبلی

در اینجا مثالی برای عناصر معتبر بیان شده:
public @interface BugReport
{
enum Status { UNCONFIRMED, CONFIRMED, FIXED, NOTABUG };
boolean showStopper() default false;
String assignedTo() default "[none]";
Class<? extends Testable> testCase() default Testable.class;
Status status() default Status.UNCONFIRMED;
TestCase testCase();
String[] reportedBy();
}
به علت اینکه annotation ها به وسیله ی کامپایلر ارزیابی می شوند، همه ی مقادیر عناصر باید در زمان کامپایل ثابت باشند. مانند:
@BugReport(showStopper=true, assignedTo="Harry", testCase=MyTestCase.class,
status=BugReport.Status.CONFIRMED, . . .)
اگر مقدار یک عنصر، یک آرایه باشد، این مقادیر را باید در {} قرار دهیم مانند زیر:
@BugReport(. . ., reportedBy={"Harry", "Carl"})
همچنین اگر عنصر فقط یک مقدار داشته باشد، می توانید {} قرار ندهید:
@BugReport(. . ., reportedBy="Joe") // OK, same as {"Joe"}
از آنجایی که یک عنصر annotation خود می تواند annotation باشد، می توانید annotation پیچیده ای داشته باشید. برای مثال:
@BugReport(testCase=@TestCase(id="3352627"), . . .)
شما می توانید annotation ها را به موارد زیر اضافه کنید:

1- پکیج ها
2- کلاسها(شامل enum)
3- اینترفیس ها(شامل اینترفیس annotation ها)
4- متد ها
5- سازنده ها
6- فیلدهای نمونه(شامل ثابت های enum)
7- متغییر های محلی
8- متغییر های پارامتر

یک آیتم می تواند annotation های چندگانه داشته باشد. زمانیکه یک آیتم خاصیannotate می کنید، نمی توانید annotation با همان نام را بیش از یکبار استفاده کنید. برای مثال:
@BugReport(showStopper=true, reportedBy="Joe")
@BugReport(reportedBy={"Harry", "Carl"})
void myMethod()
کد بالا خطای زمان کامپایل می دهد. شما می توانید annotation ای را طراحی کنید که مقدار آن آرایه ای از annotation های مشابه باشد. مانند زیر:
@BugReports({
@BugReport(showStopper=true, reportedBy="Joe"),
@BugReport(reportedBy={"Harry", "Carl"}))
void myMethod()
annotation های استاندارد
JDK 5.0 هفت اینترفیس annotation تعریف کرده است. سه تای آن annotation های معمول هستند که شما می توانید در کدهایتان استفاده کنید. چهارتای دیگر متا- annotation هستند که رفتار اینترفیس annotation را توصیف می کند. جدول زیر اینها را نشان می هد.
اکنون می خواهیم مثالی را در intellij بررسی کنیم.
فرض می کنیم کلاسی با نام example ایجاد کرده ایم که کد های درون آن به صورت زیر می باشد:
1 package first;
2
3 /**
4 * Created by IntelliJ IDEA.
5 * User: fateme
6 * Date: 1/24/14
7 * Time: 4:41 PM
8 * To change this template use File | Settings | File Templates.
9 */
10 public class example {
11 }
از خط 3 تا 9 کامنت یا توضیحات می باشد و ما می خواهیم برای آن annotation ایجاد کنیم و کامنت های ابتدای کلاس example را از طریق کلاس annotation ای به نام standardcomment بیان کنیم.
ابتدا از طریق زیر یک کلاس annotation با نام standardcomment ایجاد می کنیم و یا می توانیم یک کلاس عادی ایجاد کنیم و کد های این مثال را در آن کپی کنیم.
آموزش جاوا    SEبا -intellij  (3) - Annotation
کد های درون آن به صورت زیر می باشد: (که ما کامنت های ابتدای آن را پاک کردیم)
1 package first;
2
3 public @interface standardcomment {
4 }
اکنون قبل از نام کلاس کد زیر را وارد می کنیم:
@Retention(RetentionPolicy.RUNTIME)
برای استفاده ی این کد باید درابتدای آن دو بسته ی زیر را import کنیم:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
فرضا در کامنت کلاس example موارد زیر را داریم:

• Developername از نوع string
• Companyname از نوع string
• Date از نوع int
• Status از نوع Boolean

و می خواهیم این کامنت ها را از ابتدای کلاس example حذف کرده و آنها را بوسیله ی annotation ای که تولید کردیم، در اینجا بکار ببریم:
کد های درون annotation به صورت زیر می باشد:
1 package first;
2
3 import java.lang.annotation.Retention;
4 import java.lang.annotation.RetentionPolicy;
5
6 @Retention(RetentionPolicy.RUNTIME)
7
8 public @interface standardcomment {
9
10 public String Developername() default "Fateme";
11 public String Companyname() default "sit";
12 public int Date() default 0;
13 public boolean Status() default true;
14
15 }
و در ابتدای کلاس example بجای کامنت ها ، annotation را با مقادیر خاص خودش وارد می کنیم و کدها به صورت زیر می باشد (بعد از import ها)
زمانیکه علامت @ را تایپ می کنیم، پیشنهادات زیر داده می شود که ما standardcomment که در پکیج first وجود دارد را انتخاب می کنیم:
آموزش جاوا    SEبا -intellij  (3) - Annotation
بعد از انتخاب آن لیست آیتم های آن را نشان می دهد:
آموزش جاوا    SEبا -intellij  (3) - Annotation
با فشردن کلیدهای ctrl+space همه ی آیتم های آن را نشان می دهد که می توان آنها را انتخاب کرد و به آنها مقدار دلخواه داد و اگر به آیتمی مقدار ندهیم، مقدار پیشفرض را می پذیرد:
آموزش جاوا    SEبا -intellij  (3) - Annotation
در پایان کد کلاس example به صورت زیر می شود:
1 package first;
2
3 @standardcomment(Companyname = "rasekhoon" ,Date = 1392,Developername ="fateme",Status = false)
4
5 public class example {
6 }
استفاده از مطالب این مقاله با ذکر منبع راسخون بلامانع می باشد.



 

 



ارسال نظر
با تشکر، نظر شما پس از بررسی و تایید در سایت قرار خواهد گرفت.
متاسفانه در برقراری ارتباط خطایی رخ داده. لطفاً دوباره تلاش کنید.