כיצד לתאר את קוד Java עם ביאורים

כנראה נתקלת במצבים שבהם עליך לשייך מטא נתונים (נתונים המתארים נתונים אחרים) למחלקות, שיטות ו / או אלמנטים אחרים של יישומים. לדוגמה, ייתכן שצוות התכנות שלך יצטרך לזהות שיעורים לא גמורים ביישום גדול. עבור כל מחלקה לא גמורה, המטא-נתונים יכללו ככל הנראה את שם היזם האחראי על סיום הכיתה ותאריך סיום הצפוי של הכיתה.

לפני Java 5, הערות היו המנגנון הגמיש היחיד שהיה לג'אווה להציע לצורך שיוך מטא-נתונים לאלמנטים של יישומים. עם זאת, הערות הן בחירה גרועה. מכיוון שהמהדר מתעלם מהן, הערות אינן זמינות בזמן הריצה. וגם אם הם היו זמינים, היה צורך לנתח את הטקסט כדי להשיג פריטי נתונים מכריעים. מבלי לתקנן את אופן ציון פריטי הנתונים, ייתכן ופריטי נתונים אלה יתבררו כבלתי ניתנים לניתוח.

הורד קבל את הקוד הורד את קוד המקור לדוגמאות במדריך זה של Java 101. נוצר על ידי ג'ף פרייזן עבור.

מנגנוני הערות לא תקניים

Java מספקת מנגנונים שאינם תקניים לשייכות מטא-נתונים לאלמנטים של יישומים. לדוגמה, transientהמילה השמורה מאפשרת לך להוסיף הערות (לשייך נתונים ל) שדות שאינם נכללים במהלך סידור.

Java 5 שינתה הכל על ידי הצגת ביאורים , מנגנון סטנדרטי לשיוך מטא-נתונים לאלמנטים של יישומים שונים. מנגנון זה מורכב מארבעה מרכיבים:

  • @interfaceמנגנון הכרזת סוגי הערות.
  • סוגי הערות מטא, בהן תוכלו להשתמש כדי לזהות את רכיבי היישום עליהם חל סוג הערה; לזהות את משך חיי ההערה (מופע מסוג הערה); ועוד.
  • תמיכה בעיבוד הערות באמצעות הרחבה ל- Java Reflection API (שיידון במאמר עתידי), בה תוכלו להשתמש כדי לגלות הערות זמן ריצה של תוכנית, וכלי כללי לעיבוד הערות.
  • סוגי הערות סטנדרטיים.

אסביר כיצד להשתמש ברכיבים אלה כאשר אנו עוברים דרכנו במאמר זה.

הצהרת סוגי הערות באמצעות @interface

אתה יכול להכריז על סוג הערה על ידי ציון @הסמל ואחריו interfaceהמילה השמורה ומזהה. לדוגמה, רישום 1 מצהיר על סוג הערה פשוט שבו אתה יכול להשתמש כדי להוסיף הערה לקוד בטוח לשרשור.

רישום 1:ThreadSafe.java

public @interface ThreadSafe {}

לאחר שהצהרת על סוג ההערה הזה, קידם מראש את השיטות שלדעתך כבטוחות חוט עם מקרים מסוג זה על ידי הכנה @מיידית ואחריו שם הסוג לכותרות השיטה. רישום 2 מציע דוגמה פשוטה שבה main()מתוארת השיטה @ThreadSafe.

רישום 2:AnnDemo.java (גרסה 1)

מחלקה ציבורית AnnDemo {@ThreadSafe ציבורי ריק ריק סטטי (מחרוזת [] טענות) {}}

ThreadSafeמקרים אינם מספקים מטא נתונים מלבד שם סוג ההערה. עם זאת, באפשרותך לספק מטא-נתונים על ידי הוספת אלמנטים לסוג זה, כאשר אלמנט הוא כותרת שיטה הממוקמת בגוף סוג ההערה.

כמו גם שאין גופי קוד, אלמנטים כפופים למגבלות הבאות:

  • כותרת השיטה אינה יכולה להכריז על פרמטרים.
  • כותרת השיטה אינה יכולה לספק סעיף זריקות.
  • סוג חזרתו של כותרת השיטה חייב להיות סוג פרימיטיבי (למשל, int), java.lang.String, java.lang.Class, גידול enum, סוג ביאור, או מערך של אחד מהסוגים. לא ניתן לציין סוג אחר לסוג ההחזרה.

כדוגמה נוספת, רישום 3 מציג ToDoסוג הערה עם שלושה אלמנטים המזהים עבודת קידוד מסוימת, תוך ציון התאריך בו העבודה אמורה להסתיים, ושם הקודן האחראי להשלמת העבודה.

רישום 3:ToDo.java (גרסה 1)

ToDo ציבורי @interface {int id (); מחרוזת finishDate (); קודן מחרוזת () ברירת מחדל "לא מתאים"; }

שים לב כי כל אלמנט אינו מצהיר על פרמטר (ים) או סעיף זורק, בעל סוג החזרה חוקי ( intאו String) ומסתיים בנקודה-פסיק. כמו כן, האלמנט הסופי מגלה כי ניתן לציין ערך ברירת מחדל להחזר; ערך זה מוחזר כאשר ביאור אינו מקצה ערך לאלמנט.

רישום 4 משתמש ToDoבהערת שיטת כיתות לא גמורות.

רישום 4:AnnDemo.java (גרסה 2)

class public AnnDemo {public static void main (String [] args) {String [] cities = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; מיין (ערים); } @ TooDo (id = 1000, finishDate = "10/10/2019", coder = "John Doe") מיון ריק סטטי (אובייקט [] אובייקטים) {}}

רישום 4 מקצה פריט מטא לכל אלמנט; למשל, 1000מוקצה ל id. שלא כמו coder, יש לציין את האלמנטים idו- finishDate; אחרת, המהדר ידווח על שגיאה. כאשר coderלא מוקצה לו ערך, הוא מניח את "n/a"ערך ברירת המחדל שלו .

Java מספקת String value()אלמנט מיוחד שניתן להשתמש בו להחזרת רשימה מופרדת בפסיקים של פריטי מטא נתונים. רישום 5 מדגים אלמנט זה בגרסה מחודשת של ToDo.

רישום 5:ToDo.java (גרסה 2)

ציבור @ ממשק ToDo {ערך מחרוזת (); }

מתי value()הוא האלמנט היחיד של סוג ההערה, אינך צריך לציין valueאת =אופרטור ההקצאה בעת הקצאת מחרוזת לאלמנט זה. רישום 6 מדגים את שתי הגישות.

רישום 6:AnnDemo.java (גרסה 3)

class public AnnDemo {public static void main (String [] args) {String [] cities = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; מיין (ערים); } @ TooDo (value = "1000,10 / 10/2019, John Doe") מיון חלל סטטי (אובייקט [] אובייקטים) {} @ToDo ("1000,10 / 10/2019, John Doe") חיפוש בוליאני סטטי ( אובייקט [] אובייקטים, מפתח אובייקט) {return false; }}

שימוש בסוגי מטה-ביאור - בעיית הגמישות

אתה יכול להוסיף הערות לסוגים (למשל, מחלקות), שיטות, משתנים מקומיים ועוד. עם זאת, גמישות זו עלולה להיות בעייתית. לדוגמה, ייתכן שתרצה להגביל ToDoלשיטות בלבד, אך שום דבר אינו מונע את השימוש בה לביאור אלמנטים אחרים של יישומים, כפי שמוצג ברשימה 7.

רישום 7:AnnDemo.java (גרסה 4)

@ToDo ("1000,10 / 10/2019, John Doe") מחלקה ציבורית AnnDemo {public static void main (String [] args) {@ToDo (value = "1000,10 / 10/2019, John Doe") String [] ערים = {"ניו יורק", "מלבורן", "בייג'ינג", "מוסקבה", "פריז", "לונדון"}; מיין (ערים); } @ TooDo (value = "1000,10 / 10/2019, John Doe") מיון חלל סטטי (אובייקט [] אובייקטים) {} @ToDo ("1000,10 / 10/2019, John Doe") חיפוש בוליאני סטטי ( אובייקט [] אובייקטים, מפתח אובייקט) {return false; }}

ברשימה 7, ToDoמשמש גם לביאור AnnDemoהמחלקה citiesוהמשתנה המקומי. הימצאותן של הערות שגויות אלה עשויה לבלבל מישהו הסוקר את הקוד שלך, או אפילו כלי עיבוד ההערות שלך. בזמנים שבהם אתה צריך לצמצם את הגמישות של סוג ההערה, Java מציעה את Targetסוג ההערה java.lang.annotationבחבילה שלה .

Targetהוא סוג מטה-ביאור  - סוג ביאור שההערות שלו מבטאות את סוגי ההערות, בניגוד לסוג שאינו מטא-ביאור שההערות שלו מציינות אלמנטים של יישומים, כגון מחלקות ושיטות. הוא מזהה את סוגי רכיבי היישום שעליהם ניתן להשתמש בסוג הערה. אלמנטים אלו מזוהים על ידי Targets" ElementValue[] value()אלמנט.

java.lang.annotation.ElementTypeהוא אנום שקביעותיו מתארות מרכיבי יישום. לדוגמא, CONSTRUCTORחל על בונים PARAMETERוחל על פרמטרים. רישום 8 רקטורים רשימת ToDoסוג ההערות של 5 כדי להגביל אותו לשיטות בלבד.

רישום 8:ToDo.java (גרסה 3)

ייבא java.lang.annotation.ElementType; ייבא java.lang.annotation.Target; @Target ({ElementType.METHOD}) ציבורי @ ממשק ToDo {ערך מחרוזת (); }

בהתחשב ToDoבסוג ההערה המחודש , ניסיון לאסוף רישום 7 מביא כעת להודעת השגיאה הבאה:

AnnDemo.java:1: שגיאה: סוג ההערה אינו חל על הצהרה מסוג זה @ToDo ("1000,10 / 10/2019, John Doe") ^ AnnDemo.java:6: שגיאה: סוג ההערה אינו ישים לסוג זה של הצהרה @ TooDo (value = "1000,10 / 10/2019, ג'ון דו") ^ 2 שגיאות

Additional meta-annotation types

Java 5 introduced three additional meta-annotation types, which are found in the java.lang.annotation package:

  • Retention indicates how long annotations with the annotated type are to be retained. This type’s associated java.lang.annotation.RetentionPolicy enum declares constants CLASS (compiler records annotations in class file; virtual machine doesn’t retain them to save memory — default policy), RUNTIME (compiler records annotations in class file; virtual machine retains them), and SOURCE (compiler discards annotations).
  • Documented indicates that instances of Documented-annotated annotations are to be documented by javadoc and similar tools.
  • Inherited indicates that an annotation type is automatically inherited.

Java 8 introduced the java.lang.annotation.Repeatable meta-annotation type. Repeatable is used to indicate that the annotation type whose declaration it (meta-)annotates is repeatable. In other words, you can apply multiple annotations from the same repeatable annotation type to an application element, as demonstrated here:

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

This example assumes that ToDo has been annotated with the Repeatable annotation type.

Processing annotations

Annotations are meant to be processed; otherwise, there’s no point in having them. Java 5 extended the Reflection API to help you create your own annotation processing tools. For example, Class declares an Annotation[] getAnnotations() method that returns an array of java.lang.Annotation instances describing annotations present on the element described by the Class object.

Listing 9 presents a simple application that loads a class file, interrogates its methods for ToDo annotations, and outputs the components of each found annotation.

Listing 9:AnnProcDemo.java

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]); } } } }

After verifying that exactly one command-line argument (identifying a class file) has been specified, main() loads the class file via Class.forName(), invokes getMethods() to return an array of java.lang.reflect.Method objects identifying all public methods in the class file, and processes these methods.

Method processing begins by invoking Method’s boolean isAnnotationPresent(Class annotationClass) method to determine if the annotation described by ToDo.class is present on the method. If so, Method’s T getAnnotation(Class annotationClass) method is called to obtain the annotation.

The ToDo annotations that are processed are those whose types declare a single String value() element (see Listing 5). Because this element’s string-based metadata is comma-separated, it needs to be split into an array of component values. Each of the three component values is then accessed and output.

Compile this source code (javac AnnProcDemo.java). Before you can run the application, you’ll need a suitable class file with @ToDo annotations on its public methods. For example, you could modify Listing 6’s AnnDemo source code to include public in its sort() and search() method headers. You’ll also need Listing 10’s ToDo annotation type, which requires the RUNTIME retention policy.

Listing 10:ToDo.java (version 4)

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(); }

Compile the modified AnnDemo.java and Listing 10, and execute the following command to process AnnDemo’s ToDo annotations:

java AnnProcDemo AnnDemo

If all goes well, you should observe the following output:

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

Processing annotations with apt and the Java compiler

Java 5 הציג aptכלי לעיבוד הערות באופן כללי. Java 6 העביר aptאת הפונקציונליות javacלכלי המהדר שלה , ו- Java 7 הוצא משימוש apt, אשר הוסר לאחר מכן (החל מ- Java 8).

סוגי הערות סטנדרטיים

יחד עם Target, Retention, Documented, ו Inherited, Java 5 הציגו java.lang.Deprecated, java.lang.Override, ו java.lang.SuppressWarnings. שלושת סוגי ההערות הללו נועדו לשימוש בהקשר של מהדר בלבד, ולכן מדיניות השימור שלהם מוגדרת SOURCE.

הוצא משימוש