שינויים בממשק Java 8 כוללים שיטות סטטיות ושיטות ברירת מחדל בממשקים. לפני Java 8, הייתה לנו אפשרות להכיל רק הצהרות של שיטות בממשקים. אך מ־Java 8, אנו יכולים להכיל שיטות ברירת מחדל ו־שיטות סטטיות בממשקים.
ממשק Java 8
עיצוב ממשקים תמיד היה משימה מאתגרת מאחר ואם נרצה להוסיף שיטות נוספות בממשקים, יהיה עלינו לשנות את כל מחלקות המימוש. ככל שהממשק יגדל, מספר המחלקות המיישמות אותו עשוי לגדול למדרגה שבה לא תהיה אפשרות להרחיב ממשקים. לכן, בעת עיצוב אפליקציה, רוב הפריימוורקים מספקים מחלקת מימוש בסיסית ואז אנו מרחיבים אותה ומחליפים שיטות שניתן להחליף עבור האפליקציה שלנו. בואו נסתכל על שיטות ממשק ברירת מחדל ושיטות ממשק סטטיות ועל הסיבות להצגתן בשינויים בממשק Java 8.
שיטת ברירת המחדל בממשק Java
ליצירת שיטת ברירת מחדל בממשק ג'אווה, עלינו להשתמש במילת המפתח "default" יחד עם חתימת השיטה. לדוגמה,
package com.journaldev.java8.defaultmethod;
public interface Interface1 {
void method1(String str);
default void log(String str){
System.out.println("I1 logging::"+str);
}
}
שימו לב ש- log(String str) היא שיטת ברירת המחדל בממשק Interface1. כעת כאשר מחלקה תממש את Interface1, אין חובה לספק ביצוע עבור שיטות ברירת המחדל של הממשק. התכונה הזו תעזור לנו בהרחבת ממשקים עם שיטות נוספות, הכל שעלינו לעשות הוא לספק ביצוע ברירת המחדל. נניח שיש לנו ממשק נוסף עם השיטות הבאות:
package com.journaldev.java8.defaultmethod;
public interface Interface2 {
void method2();
default void log(String str){
System.out.println("I2 logging::"+str);
}
}
אנו יודעים שב-Java אין לנו אפשרות להרחיב מספר רב של מחלקות משום שזה יגרום ל"בעיה של המגן" שבה המהדר לא יוכל להחליט איזו שיטת מערכת העל יש להשתמש. עם שיטות ברירת המחדל, בעיה זו תתפתח גם בממשקים. מכיוון שאם מחלקה מממשת גם את Interface1 וגם את Interface2 ולא מממשת את שיטת ברירת המחדל המשותפת, המהדר לא יוכל להחליט איזו לבחור. הרחבת מספר רב של ממשקים היא חלק חשוב ב-Java, ותמצאו אותה במחלקות הקור Java הבסיסיות כמו גם ברוב היישומים העסקיים והמסגרות. לכן, כדי לוודא שהבעיה הזו לא תתרחש בממשקים, הוא הופך להיות חובה לספק ביצוע עבור שיטות ברירת המחדל המשותפות של הממשקים. לכן, אם מחלקה מממשת את שני הממשקים הנ"ל, עליה לספק ביצוע עבור שיטת log() אחרת המהדר יזרוק שגיאת זמן קידום. מחלקה פשוטה שמממשת גם את Interface1 וגם את Interface2 תהיה:
package com.journaldev.java8.defaultmethod;
public class MyClass implements Interface1, Interface2 {
@Override
public void method2() {
}
@Override
public void method1(String str) {
}
@Override
public void log(String str){
System.out.println("MyClass logging::"+str);
Interface1.print("abc");
}
}
נקודות חשובות בנוגע לשיטות ברירת המחדל של ממשקי Java:
- שיטות ברירת מחדל של ממשקי Java יעזרו לנו להרחיב ממשקים מבלי לפגוע במחלקות היישום.
- שיטות ברירת המחדל של ממשקי Java יירדו את ההבדלים בין ממשקים ובין מחלקות מופשטות.
- שיטות ברירת המחדל של ממשקי Java 8 יעזרו לנו להימנע ממחלקות ייחודיות, כגון כל השיטות של מחלקת האוספים יכולות להיספק בממשקים עצמם.
- שיטות ברירת המחדל של ממשקי Java יעזרו לנו להסיר מחלקות בסיסיות ליישום, נוכל לספק ביצוע ברירת מחדל והמחלקות המיישמות יכולות לבחור איזו מהן לדרוס.
- אחת הסיבות העיקריות להכנסת שיטות ברירת המחדל בממשקים היא לשפר את API של מחלקות האוספים ב-Java 8 כדי לתמוך בביטויי למבדרים.
- אם יש למחלקה במצערה שיטה עם אותו החתימה, אז שיטות ברירת המחדל משתעלות. שיטה ברירת מחדל לא יכולה לדרוס שיטה מתוך
java.lang.Object
. הסיבה פשוטה מאוד, כי אובייקט הוא מחלקת הבסיס לכל המחלקות ב-Java. אז גם אם יש לנו שיטות של מחלקת Object מוגדרות כשיטות ברירת המחדל בממשקים, זה יהיה ללא שימוש מכיוון ששיטת מחלקת Object תמיד תשמש. לכן כדי למנוע בלבול, אין לנו אפשרות לקבוע שיטות ברירת מחדל שמדרסות שיטות של מחלקת Object. - שיטות ברירת המחדל של ממשקי Java מכונות גם כשיטות הגנה או שיטות הרחבה וירטואליות.
שיטת מחלקת ממשק Java סטטית
שיטת הממשק של Java היא דומה לשיטת ברירת המחדל חוץ מכך שאין באפשרותנו לדרוס אותם בכיתות המימוש. תכונה זו עוזרת לנו למנוע תוצאות לא רצויות במקרה של מימוש חלש בכיתות המימוש. בואו נסתכל על דבר זה עם דוגמה פשוטה.
package com.journaldev.java8.staticmethod;
public interface MyData {
default void print(String str) {
if (!isNull(str))
System.out.println("MyData Print::" + str);
}
static boolean isNull(String str) {
System.out.println("Interface Null Check");
return str == null ? true : "".equals(str) ? true : false;
}
}
עכשיו בואו נסתכל על כיתת מימוש שיש לה את השיטה isNull() עם מימוש חלש.
package com.journaldev.java8.staticmethod;
public class MyDataImpl implements MyData {
public boolean isNull(String str) {
System.out.println("Impl Null Check");
return str == null ? true : false;
}
public static void main(String args[]){
MyDataImpl obj = new MyDataImpl();
obj.print("");
obj.isNull("abc");
}
}
שימו לב שisNull(String str) הוא שיטת כיתה פשוטה, זה לא דריסת שיטת הממשק. לדוגמה, אם נוסיף את האנוטציה @Override לשיטת isNull(), נקבל שגיאת קומפילציה. כעת כאשר נפעיל את היישום, נקבל את הפלט הבא.
Interface Null Check
Impl Null Check
אם נשנה את השיטה מסטטית לברירת המחדל, נקבל את הפלט הבא.
Impl Null Check
MyData Print::
Impl Null Check
שיטת הממשק הסטטית של Java גלויה לשיטות הממשק בלבד, אם נסיר את השיטה isNull() מכיתת MyDataImpl, לא נוכל להשתמש בה עבור אובייקט MyDataImpl. עם זאת, כמו שיטות אחרות שהן סטטיות, אנו יכולים להשתמש בשיטות סטטיות של ממשק באמצעות שם המחלקה. לדוגמה, פקודה חוקית תהיה:
boolean result = MyData.isNull("abc");
נקודות חשובות על שיטת הממשק הסטטית של Java:
- שיטת הממשק הסטטית של Java היא חלק מהממשק, לא נוכל להשתמש בה עבור אובייקטי מימוש.
- שיטות הממשק הסטטיות של Java טובות לספק שיטות שימושיות, לדוגמה בדיקת null, מיון אוספים וכו'.
- פונקציה סטטית של ממשק Java עוזרת לנו לספק אבטחה על ידי אי אפשרות למחלקות המממשות לדרוס אותן.
- לא ניתן להגדיר פונקציה סטטית של ממשק למתודות של מחלקת Object, נקבל שגיאת מהדר כ"הפונקציה הסטטית לא יכולה להסתיר את הפונקציה המופעית ממחלקת Object". הסיבה לכך היא שזה אינו מותר ב-Java, מאחר ו-Object היא המחלקה הבסיסית לכל המחלקות ואי אפשר להגדיר פונקציה סטטית ברמת המחלקה ומתודת מופע בעל אותה החתימה.
- ניתן להשתמש בפונקציות סטטיות של ממשק Java כדי להסיר ממחלקות יילות כמו Collections ולהעביר את כל הפונקציות הסטטיות שלה לממשק המתאים, וזה יהיה קל למצוא ולהשתמש בהן.
ממשקי פונקציות ב-Java
לפני שאני מסיים את הפוסט, אני רוצה לספק הקדמה קצרה לממשקי פונקציונליות. ממשק שיש בו בדיוק אחת מתודה מופשטת ידוע כממשק פונקציונלי. הוספנו אנוטציה חדשה @FunctionalInterface כדי לסמן ממשק כממשק פונקציונלי. אנטוטציית @FunctionalInterface היא אפשרות למניעת הוספת שגיאתית של מתודות מופשטות בממשקי הפונקציונליות. זה אופציונלי אך נהוג להשתמש בו. ממשקי פונקציונליות הם תכונה רבת הצפייה ומחכה עוד מאוד של Java 8 מכיוון שהוא מאפשר לנו להשתמש בביטויי למבדר lambda expressions כדי להפעיל אותם. חבילת java.util.function
עם מספר ממשקי פונקציונליות נוספו כדי לספק סוגי יעד לביטויי למבדר ולייחוסי שיטה. נסתכל על ממשקי פונקציונליות וביטויי למבדר בפוסטים הבאים.