JavaRush /בלוג Java /Random-HE /שיטות ברירת מחדל ב-Java 8: מה הם יכולים ומה הם לא יכולים ...
Spitfire
רָמָה

שיטות ברירת מחדל ב-Java 8: מה הם יכולים ומה הם לא יכולים לעשות?

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

מהי שיטת ברירת המחדל

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

ירושה מרובה?

הדברים מסתבכים יותר אם מחלקה מיישמת יותר מממשק אחד (נניח, שניים), והם מיישמים את אותה שיטת ברירת מחדל. איזו שיטה תירש המחלקה? התשובה היא אף אחת. במקרה זה, המחלקה חייבת ליישם את השיטה עצמה (בין אם ישירות או על ידי ירושה ממחלקה אחרת). המצב דומה אם רק לממשק אחד יש שיטת ברירת מחדל, ובשני אותה שיטה מופשטת. Java 8 מנסה להיות ממושמע ולהימנע ממצבים מעורפלים. אם מתודות מוצהרות ביותר מממשק אחד, אזי לא ירש המחלקה יישום ברירת מחדל - תקבל שגיאת קומפילציה. אמנם, ייתכן שלא תקבל שגיאת קומפילציה אם הכיתה שלך כבר מורכבת. Java 8 אינו חזק מספיק בהקשר זה. יש לכך סיבות, שאני לא רוצה להיכנס לדיון בהן (למשל: מהדורת ה-Java כבר שוחררה והזמן לדיונים עבר מזמן ובכלל, זה לא המקום עבורם).
  • נניח שיש לך שני ממשקים ומחלקה מיישמת את שניהם.
  • אחד הממשקים מיישם את שיטת ברירת המחדל m().
  • אתה מרכיב את כל הממשקים והכיתה.
  • אתה משנה ממשק שאין לו מתודה m() על ידי הכרזה עליו כשיטה מופשטת.
  • אתה מרכיב רק את הממשק שהשתנה.
  • התחל את השיעור.
שיטות ברירת מחדל ב-Java 8: מה הם יכולים ומה הם לא יכולים לעשות?  - 2במקרה זה הכיתה עובדת. אתה לא יכול להרכיב אותו עם הממשקים המעודכנים, אבל הוא הידור עם גרסאות ישנות יותר ולכן עובד. עַכשָׁיו
  • שנה את הממשק עם שיטת m() abstract והוסף מימוש ברירת מחדל.
  • הרכיב את הממשק שהשתנה.
  • הפעל מחלקה: שגיאה.
כאשר ישנם שני ממשקים המספקים מימוש ברירת מחדל של מתודה, לא ניתן לקרוא לשיטה זו במחלקה אלא אם כן היא מיושמת על ידי המחלקה עצמה (שוב, לבד או בירושה ממחלקה אחרת). שיטות ברירת מחדל ב-Java 8: מה הם יכולים ומה הם לא יכולים לעשות?  - 3תואם מחלקה. ניתן לטעון אותו עם ממשק שונה. זה יכול אפילו לפעול עד שנקרא שיטה שיש לה מימוש ברירת מחדל בשני הממשקים.

קוד לדוגמה

שיטות ברירת מחדל ב-Java 8: מה הם יכולים ומה הם לא יכולים לעשות?  - 4כדי להדגים את האמור לעיל, יצרתי ספריית בדיקה למחלקה C.java ו-3 ספריות משנה עבור הממשקים בקבצים I1.java ו-I2.java. ספריית השורש של הבדיקה מכילה את קוד המקור של המחלקה C.java. ספריית הבסיס מכילה גרסה של הממשקים המתאימים לביצוע והידור: לממשק I1 יש שיטת ברירת מחדל m(); לממשק I2 אין עדיין שיטות. למחלקה יש שיטה mainכדי שנוכל להפעיל אותה כדי לבדוק אותה. הוא בודק אם יש ארגומנטים של שורת פקודה, כך שנוכל להפעיל אותו בקלות עם או בלי לקרוא את ה- m().
~/github/test$ cat C.java
public class C implements I1, I2 {
  public static void main(String[] args) {
    C c = new C();
    if( args.length == 0 ){
      c.m();
    }
  }
}
~/github/test$ cat base/I1.java
public interface I1 {
  default void m(){
    System.out.println("hello interface 1");
  }
}
~/github/test$ cat base/I2.java
public interface I2 {
}
אתה יכול לקמפל ולהריץ את המחלקה משורת הפקודה.
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
הספרייה התואמת מכילה גרסה של ממשק I2 שמצהירה על שיטת m() אבסטרקטית, וגם, מסיבות טכניות, עותק לא שונה של I1.java.
~/github/test$ cat compatible/I2.java

public interface I2 {
  void m();
}
לא ניתן להשתמש בערכה כזו כדי להרכיב מחלקה C:
~/github/test$ javac -cp .:compatible C.java
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
       ^
1 error
הודעת השגיאה מאוד מדויקת. עם זאת, יש לנו C.class מקומפילציה קודמת, ואם נקמפל את הממשקים לתוך הספרייה התואמת, יהיו לנו שני ממשקים שעדיין יכולים לשמש להפעלת המחלקה:
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
הספרייה השלישית - wrong- מכילה את גרסה I2, שגם מכריזה על השיטה m():
~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}
אתה אפילו לא צריך לדאוג לגבי קומפילציה. למרות שהמתודה הוכרזה פעמיים, עדיין ניתן להשתמש במחלקה ולהפעיל אותה עד שהמתודה m() תיקרא. בשביל זה אנחנו צריכים את ארגומנט שורת הפקודה:
~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: I1.m I2.m
    at C.m(C.java)
    at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$

סיכום

כאשר אתה מעביר את הספרייה שלך ל-Java 8 ומשנה את הממשקים שלך כך שיכללו שיטות ברירת מחדל, כנראה שלא יהיו לך בעיות. לפחות, זה מה שמפתחי ספריית Java 8 מקווים כשהם מוסיפים פונקציונליות. יישומים המשתמשים בספרייה שלך עדיין משתמשים בה עבור Java 7, שבה אין שיטות ברירת מחדל. אם נעשה שימוש בכמה ספריות יחד, קיימת אפשרות להתנגשות. איך להימנע מזה? עצב את ה-API של הספרייה שלך באותו אופן כמו קודם. אל תהפוך שאנן על ידי הסתמכות על היכולות של שיטות ברירת המחדל. הם מוצא אחרון. בחר שמות בקפידה כדי למנוע התנגשויות עם ממשקים אחרים. בואו נראה כיצד יתפתח פיתוח עבור Java באמצעות תכונה זו.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION