JDK 7: מפעיל היהלומים

Project Coin מספק "שיפורי שפות קטנות" רבות בתור קבוצת משנה של התכונות החדשות של JDK 7. לאחרונה כתבתי בלוג על הפעלת מיתרים של Project Coin ובפוסט זה אני כותב על מפעיל היהלומים החדש ( ).

מפעיל היהלומים מצמצם חלק מהפרשנות הג'אווה סביב הגנריות על ידי כך שהמהדר מסיק את סוגי הפרמטרים לבנאים של מחלקות גנריות. ההצעה המקורית להוספת מפעיל היהלומים לשפת ג'אווה הועלתה בפברואר 2009 וכוללת דוגמה פשוטה זו:

לדוגמה, שקול את הצהרת המטלה הבאה:

מַפָּה אנגרמות = HashMap חדש ();

זה ארוך למדי, כך שניתן להחליפו בזה:

מַפָּה אנגרמות = HashMap חדש ();

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

הדרישה שפרמטרים מסוג ישוכפלו שלא לצורך כמו

זה מעודד אומלל

שפע יתר של שיטות מפעל סטטיות, פשוט בגלל היסק מסוג

עובד על קריאות שיטה.

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

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

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

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

final Map
     
       statesToCities = new HashMap(); // raw! 
     

שתי תמונות המסך הבאות מציגות את תגובת המהדר לשורת הקוד הנ"ל. התמונה הראשונה מציגה את ההודעה כאשר אין מופעלת אזהרות -Xlint והשנייה מציגה את האזהרה המפורשת יותר המתרחשת כאשר -Xlint:uncheckedניתנת כארגומנט ל- javac.

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

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

package dustin.examples; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import static java.lang.System.out; /** * Very simple demonstration of JDK 7's/Project Coin's "Diamond Operator." */ public class DiamondOperatorDemo { /** Use of "raw" type. */ private static Set rawWithoutExplicitTyping() { final Set names = new HashSet(); addNames(names); return names; } /** Explicitly specifying generic class's instantiation parameter type. */ private static Set explicitTypingExplicitlySpecified() { final Set names = new HashSet(); addNames(names); return names; } /** * Inferring generic class's instantiation parameter type with JDK 7's * 'Diamond Operator.' */ private static Set explicitTypingInferredWithDiamond() { final Set names = new HashSet(); addNames(names); return names; } private static void addNames(final Set namesToAddTo) { namesToAddTo.add("Dustin"); namesToAddTo.add("Rett"); namesToAddTo.add("Homer"); } /** * Main executable function. */ public static void main(final String[] arguments) { out.println(rawWithoutExplicitTyping()); out.println(explicitTypingExplicitlySpecified()); out.println(explicitTypingInferredWithDiamond()); } } 

כאשר מקודדים הקוד לעיל, רק המקרה "הגולמי" מוביל לאזהרה.

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

javap -v -p -classpath classes dustin.examples.DiamondOperatorDemo 

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

מלבד שמות השיטות עצמן, אין הבדל javapבפלט. הסיבה לכך היא מחיקת סוג הגנריות של Java פירושה שההבחנה המבוססת על סוג אינה זמינה בזמן הריצה. מדריך Java בנושא גנריות כולל דף בשם Type Erasure שמסביר זאת:

המהדר מסיר את כל המידע אודות ארגומנט הסוג בפועל בזמן הקומפילציה.

מחיקת סוג קיימת כך שקוד חדש עשוי להמשיך להתממשק עם קוד מדור קודם. שימוש בסוג גולמי מכל סיבה אחרת נחשב לתרגול רע בתכנות ויש להימנע ממנו במידת האפשר.

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

סיכום

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

פרסום מקורי זמין ב // marxsoftware.blogspot.com/

סיפור זה, "JDK 7: מפעיל היהלומים" פורסם במקור בהוצאת JavaWorld.