Menu

Method Overloading ב-Java: אותו שם, פרמטרים שונים

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

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

שם אחד, כמה גרסאות

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

זו הסיבה ש-System.out.println מדפיסה בשמחה int, String, boolean או double: אין println אחת, יש הרבה גרסאות מועמסות שחולקות את השם. כותבים את הקריאה שמתכוונים אליה, והמהדר מתאים אותה לגרסה שמתאימה.

שתי המתודות נקראות square, אבל אחת מקבלת int והשנייה double. הליטרל 5 הוא int, ולכן הגרסה הראשונה רצה; 2.5 הוא double, ולכן השנייה רצה.

מה נחשב גרסה מועמסת שונה

גרסאות מועמסות חייבות להיבדל ברשימת הפרמטרים, כלומר לפחות באחד מאלה:

  • מספר פרמטרים שונה,
  • טיפוסי פרמטרים שונים, או
  • סדר טיפוסים שונה.

לכל קריאה יש רשימת ארגומנטים שמתאימה בדיוק לאחת משלוש מתודות ה-join, כך שאין בלבול.

טיפוס ההחזרה לא נחשב

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

// לא עובר קומפילציה: אותו שם, אותם פרמטרים, רק טיפוס ההחזרה שונה
static int   value() { return 1; }
static double value() { return 1.0; }   // error: value() כבר מוגדרת

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

איך Java בוחרת גרסה מועמסת

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

show(7) מתאימה בדיוק ל-int, למרות ש-long ו-double יכולים גם הם להכיל 7 אחרי הרחבה. רק אם הגרסה המדויקת הייתה נמחקת, המהדר היה מרחיב את int ל-long, ואז ל-double. הבחירה הזו מתקבלת כולה בזמן קומפילציה, לפי הטיפוסים המוצהרים של הארגומנטים.

היזהרו מקריאות עמומות

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

static void handle(String s) { }
static void handle(StringBuilder b) { }

handle(null);   // error: ההפניה ל-handle עמומה

שתי הגרסאות מקבלות null, ואף אחת אינה ספציפית יותר, ולכן הקריאה לא עוברת קומפילציה. מתקנים את זה בכך שהופכים את הטיפוס למפורש עם המרה, handle((String) null), או בתכנון מחדש כך שהגרסאות לא יתנגשו. אותה זהירות נדרשת כשמערבבים autoboxing והרחבה; שמרו על קבוצות גרסאות פשוטות מספיק כדי שלכל קריאה יהיה מנצח אחד ברור.

העמסת בנאים

העמסה לא מוגבלת למתודות רגילות: בנאים משתמשים בה כל הזמן כדי להציע כמה דרכים לבנות אובייקט. בנאי בלי ארגומנטים יכול להעביר את העבודה לבנאי מלא יותר עם this(...):

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

Overloading מול Overriding

השניים נשמעים דומים, אבל אין ביניהם קשר:

  • Overloading (העמסה): אותו שם, רשימות פרמטרים שונות, באותה מחלקה. המהדר בוחר את הגרסה בזמן קומפילציה. זה עניין של הצעת גרסאות של פעולה.
  • Overriding (דריסה): תת-מחלקה מגדירה מחדש מתודה שירשה עם אותו שם ואותם פרמטרים. Java בוחרת את הגרסה בזמן ריצה לפי הטיפוס האמיתי של האובייקט. זה עניין של החלפת התנהגות (תפגשו את זה בירושה ובפולימורפיזם).

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

הבא: Varargs

העמסה מאפשרת לכתוב את join(a, b) ואת join(a, b, c) כמתודות נפרדות, אבל מה אם רוצים לקבל מספר כלשהו של ארגומנטים בלי להצהיר על גרסה לכל כמות? תחביר ה-varargs של Java מאפשר למתודה אחת לקבל רשימת ארגומנטים באורך משתנה, וזה העמוד הבא.

שאלות נפוצות

מה זה method overloading ב-Java?

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

האם שתי מתודות יכולות להיבדל רק בטיפוס ההחזרה ב-Java?

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

מה ההבדל בין overloading ל-overriding ב-Java?

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

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

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

להתחיל