מטפל בשיטה בג'אווה

1. הקדמה

במאמר זה נבחן ממשק API חשוב שהוצג בג'אווה 7 ומשופר בגרסאות הבאות, ה- java.lang.invoke.MethodHandles.

בפרט נלמד מהן ידיות השיטה, כיצד ליצור אותן וכיצד להשתמש בהן.

2. מה הם ידיות שיטה?

מגיע להגדרתו, כאמור בתיעוד ה- API:

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

בצורה פשוטה יותר, ידיות השיטה הן מנגנון ברמה נמוכה למציאת, התאמה והפעלת שיטות.

ידיות השיטה אינן ניתנות לשינוי ואין להן מצב גלוי.

ליצירה ושימוש ב- שיטת טיפול, נדרשים 4 שלבים:

  • יצירת חיפוש
  • יצירת סוג השיטה
  • מציאת ידית השיטה
  • קריאת ידית השיטה

2.1. ידיות שיטה לעומת השתקפות

הוצגו ידיות שיטה במטרה לעבוד לצד הקיים java.lang.reflect API, מכיוון שהם משרתים מטרות שונות ויש להם מאפיינים שונים.

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

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

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

שיש הגדרה ברורה ומטרות של שיטה ידיות API, כעת אנו יכולים להתחיל לעבוד איתם, החל מהבדיקה.

3. יצירת ה הבט מעלה

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

דרך ה שיטה ידיות API, אפשר ליצור את אובייקט החיפוש, עם מצבי גישה שונים.

בואו ניצור את בדיקת המידע המספקת גישה אל פּוּמְבֵּי שיטות:

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup ();

עם זאת, למקרה שאנחנו רוצים לקבל גישה גם פְּרָטִי ו מוּגָן נוכל להשתמש במקום זאת ב- הבט מעלה() שיטה:

MethodHandles.Lookup lookup = MethodHandles.lookup ();

4. יצירת א MethodType

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

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

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

באותה דרך כמו שיטת טיפול, אפילו המקרים של א MethodType הם בלתי ניתנים לשינוי.

בואו נראה איך אפשר להגדיר a MethodType המציין א java.util.List מחלקה כסוג החזרה ו- לְהִתְנַגֵד מערך כסוג קלט:

MethodType mt = MethodType.methodType (List.class, Object []. Class);

במקרה שהשיטה מחזירה סוג פרימיטיבי או בָּטֵל כסוג ההחזרה שלו, נשתמש במחלקה המייצגת את אותם סוגים (void.class, int.class ...).

בואו נגדיר א MethodType שמחזירה ערך int ומקבלת לְהִתְנַגֵד:

MethodType mt = MethodType.methodType (int.class, Object.class);

כעת נוכל להמשיך ליצור שיטת טיפול.

5. מציאת א שיטת טיפול

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

בפרט, מפעל החיפושים מספק מערך שיטות המאפשרים לנו למצוא את ידית השיטה בצורה מתאימה בהתחשב בהיקף השיטה שלנו. החל מהתרחיש הפשוט ביותר, בואו נחקור את אלה העיקריים.

5.1. ידית שיטה לשיטות

משתמש ב findVirtual () השיטה מאפשרת לנו ליצור MethodHandle לשיטת אובייקט. בואו ניצור אחד, על בסיס concat () שיטת ה- חוּט מעמד:

MethodType mt = MethodType.methodType (String.class, String.class); MethodHandle concatMH = publicLookup.findVirtual (String.class, "concat", mt);

5.2. ידית שיטה לשיטות סטטיות

כאשר אנו רוצים לקבל גישה לשיטה סטטית, נוכל להשתמש ב- findStatic () שיטה:

MethodType mt = MethodType.methodType (List.class, Object []. Class); MethodHandle asListMH = publicLookup.findStatic (Arrays.class, "asList", mt);

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

5.3. ידית שיטה לבנאים

השגת גישה לבנאי יכולה להיעשות באמצעות findConstructor () שיטה.

בואו ניצור מטפלים בשיטה שמתנהגים כבנאי של ה- מספר שלם כיתה, קבלת א חוּט תְכוּנָה:

MethodType mt = MethodType.methodType (void.class, String.class); MethodHandle newIntegerMH = publicLookup.findConstructor (Integer.class, mt);

5.4. ידית שיטה לשדות

באמצעות ידית שיטה ניתן לקבל גישה גם לשדות.

נתחיל להגדיר את סֵפֶר מעמד:

ספר בכיתה ציבורית {מזהה מחרוזת; כותרת מחרוזת; // בונה}

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

MethodHandle getTitleMH = lookup.findGetter (Book.class, "title", String.class);

לקבלת מידע נוסף אודות הטיפול במשתנים / שדות, עיין במערכת ה- Java 9 Variable Handles Demystified, שם אנו דנים ב- API java.lang.invoke.VarHandle, שנוסף ב- Java 9.

5.5. ידית שיטה לשיטות פרטיות

יצירת ידית שיטה לשיטה פרטית יכולה להיעשות, בעזרת ה- java.lang.reflect ממשק API.

נתחיל להוסיף a פְּרָטִי שיטה ל סֵפֶר מעמד:

private String formatBook () {return id + ">" + title; }

כעת אנו יכולים ליצור ידית שיטה המתנהגת בדיוק כמו ה- formatBook () שיטה:

שיטת formatBookMethod = Book.class.getDeclaredMethod ("formatBook"); formatBookMethod.setAccessible (נכון); MethodHandle formatBookMH = lookup.unreflect (formatBookMethod);

6. הפעלת ידית שיטה

לאחר שיצרנו את ידיות השיטה שלנו, השימוש בהן הוא השלב הבא. בפרט, ה שיטת טיפול מחלקה מספקת 3 דרכים שונות לבצע ידית שיטה: לעורר(), invokeWithArugments () ו הפעל את Exact ().

נתחיל עם ה- לעורר אוֹפְּצִיָה.

6.1. הפעלת ידית שיטה

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

בואו נראה איך אפשר להשתמש ב- לעורר() עם טיעון מוסגר:

MethodType mt = MethodType.methodType (String.class, char.class, char.class); MethodHandle להחליףMH = publicLookup.findVirtual (String.class, "החלף", mt); פלט מחרוזת = (String) להחליף MH.invoke ("jovo", Character.valueOf ('o'), 'a'); assertEquals ("java", פלט);

במקרה זה, החלף MH דורש לְהַשְׁחִיר טיעונים, אבל לעורר() מבצע unboxing ב- אופי טיעון לפני ביצועו.

6.2. קורא לוויכוחים

הפעלת ידית שיטה באמצעות invokeWithArguments השיטה, היא הפחות מגבילה מבין שלוש האפשרויות.

למעשה, הוא מאפשר הפעלת arity משתנה, בנוסף לליהוק ואגרוף / ביטול איגרוף של הטיעונים ושל סוגי ההחזרה.

בא להתאמן, זה מאפשר לנו ליצור רשימה שֶׁל מספר שלם החל מ- מַעֲרָך שֶׁל int ערכים:

MethodType mt = MethodType.methodType (List.class, Object []. Class); MethodHandle asList = publicLookup.findStatic (Arrays.class, "asList", mt); רשימת רשימה = (רשימה) asList.invokeWithArguments (1,2); assertThat (Arrays.asList (1,2), הוא (רשימה));

6.3. קורא מדויק

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

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

בואו נראה איך אנחנו יכולים סְכוּם שתיים int ערכים באמצעות ידית שיטה:

MethodType mt = MethodType.methodType (int.class, int.class, int.class); MethodHandle sumMH = lookup.findStatic (Integer.class, "sum", mt); סכום int = (int) sumMH.invokeExact (1, 11); assertEquals (12, סכום);

אם במקרה זה, אנו מחליטים לעבור ל להפעיל מדויק שיטה מספר שאינו int, הקריאה תוביל ל WrongMethodTypeException.

7. עבודה עם מערך

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

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

בואו נראה כיצד אנו יכולים להפיץ ידית שיטה כדי לבדוק אם האלמנטים במערך שווים:

MethodType mt = MethodType.methodType (בוליאני.קלאס, אובייקט.קלאס); MethodHandle שווה = publicLookup.findVirtual (String.class, "שווה", mt); MethodHandle methodHandle = equals.asSpreader (אובייקט []. Class, 2); assertTrue ((בוליאני) methodHandle.invoke (אובייקט חדש [] {"java", "java"}));

8. שיפור ידית שיטה

לאחר שהגדרנו ידית שיטה, ניתן לשפר אותה על ידי קשירת ידית השיטה לוויכוח מבלי להפעיל אותה בפועל.

לדוגמא, ב- Java 9 משתמשים בהתנהגות מסוג זה לצורך אופטימיזציה חוּט שִׁרשׁוּר.

בואו נראה כיצד נוכל לבצע שרשור, המחייב סיומת לשלנו concatMH:

MethodType mt = MethodType.methodType (String.class, String.class); MethodHandle concatMH = publicLookup.findVirtual (String.class, "concat", mt); MethodHandle bindedConcatMH = concatMH.bindTo ("שלום"); assertEquals ("שלום עולם!", bindedConcatMH.invoke ("עולם!"));

9. Java 9 שיפורים

עם Java 9, מספר שיפורים נעשו ב- שיטה ידיות ממשק API במטרה להקל על השימוש בהרבה.

השיפורים השפיעו על 3 נושאים עיקריים:

  • פונקציות חיפוש - מתיר חיפוש בכיתות מהקשרים שונים ותומך בשיטות לא מופשטות בממשקים
  • טיפול בוויכוחים - שיפור קיפול הטיעונים, איסוף ויכוחים והפצת פונקציות
  • שילובים נוספים - הוספת לולאות (לוּלָאָה, תוך לולאה, doWhileLoop ...) ותמיכה חריגה טובה יותר בטיפול עם ה- נסה סוף סוף

שינויים אלה הביאו למספר יתרונות נוספים:

  • העלאות אופטימיזציה של מהדר JVM מוגברות
  • הפחתה מיידית
  • מאפשר דיוק בשימוש ב- שיטה ידיות ממשק API

פרטים על השיפורים שבוצעו זמינים באתר שיטה ידיות API Javadoc.

10. מסקנה

במאמר זה סקרנו את שיטה ידיות API, מה הם ואיך נוכל להשתמש בהם.

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

כמו תמיד, קוד המקור השלם למאמר זה זמין באתר Github.


$config[zx-auto] not found$config[zx-overlay] not found