Menu

Java Interfaces: ממשקים, חוזים ו-implements

מה זה ממשק (interface) ב-Java, איך מגדירים וממשים אותו, מתודות default ו-static, ובמה ממשקים שונים ממחלקות מופשטות.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

ממשק הוא חוזה

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

Circle implements Shape אומר ש-Circle חייבת להגדיר את area ואת perimeter. ברגע שהיא עושה זאת, Circle הוא Shape ואפשר לשמור אותו במשתנה מטיפוס Shape.

הרבה מחלקות, טיפוס אחד

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

הלולאה אף פעם לא שואלת "זה Circle או Rectangle?". היא פשוט קוראת ל-area() וסומכת על החוזה. הוספת Triangle בהמשך לא דורשת שום שינוי כאן.

מימוש כמה ממשקים

מחלקה מרחיבה רק מחלקה אחת, אבל היא יכולה לממש כמה ממשקים שתרצו. כך Java מאפשרת "ירושה מרובה" של התנהגות בצורה בטוחה:

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

מתודות default ו-static

מאז Java 8 ממשק יכול לכלול גופי מתודות. מתודת default נותנת למחלקות המממשות מימוש מוכן שהן יורשות בחינם (ויכולות לדרוס):

מתודות default מאפשרות לממשק להתפתח: להוסיף מתודה בלי לשבור כל מחלקה שכבר מממשת אותו. מתודת ממשק static, לעומת זאת, מתנהגת כמו איברים סטטיים אחרים: זו פונקציית עזר שקוראים לה על הממשק עצמו, כמו Comparator.naturalOrder().

קבועים

שדות בממשק הם באופן מובלע public static final: הם קבועים, לא מצב של מופע:

interface Physics {
    double GRAVITY = 9.81;   // automatically public static final
}

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

ממשקים פונקציונליים

ממשק עם מתודה מופשטת אחת בדיוק הוא ממשק פונקציונלי (functional interface), ואפשר לממש אותו עם ביטוי למדא במקום מחלקה שלמה:

זה הבסיס של הלמדות ב-Java ושל הטיפוסים ב-java.util.function (Function, Predicate, Supplier וחבריהם). האנוטציה @FunctionalInterface הופכת את הכוונה למפורשת ומאפשרת לקומפיילר לאכוף את כלל המתודה היחידה.

ממשק מול מחלקה מופשטת

שניהם מאפשרים לתכנת מול הפשטה, אז מתי בוחרים בכל אחד?

  • ממשק: יכולת שמחלקות לא קשורות יכולות לחלוק. גם Bird וגם Airplane יכולים להיות Flyable. מחלקה יכולה לממש הרבה ממשקים. אין מצב של מופע.
  • מחלקה מופשטת: מצב וקוד משותפים בין מחלקות קרובות. גם Cat וגם Dog מרחיבים את Animal, ויורשים שדות משותפים ומתודות שממומשות בחלקן. מחלקה מרחיבה רק מחלקה אחת.

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

הבא בתור: מחלקות מופשטות

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

שאלות נפוצות

מה זה ממשק (interface) ב-Java?

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

מה ההבדל בין ממשק למחלקה מופשטת ב-Java?

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

האם לממשק ב-Java יכולים להיות גופי מתודות?

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

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל