ג'אווה - תכנות מונחה עצמים - מתודות - דריסה/חפיפה,...

7

Upload: -

Post on 29-Jun-2015

903 views

Category:

Education


2 download

DESCRIPTION

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

TRANSCRIPT

Page 2: ג'אווה - תכנות מונחה עצמים - מתודות - דריסה/חפיפה, העמסה ומתודות בונות

Method Overriding –דריסת מתודות

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

וכמו כן, בפרק בנושא מחלקות פנימיות אנונימיות כשדרסנו בטווח ההגדרה של ()equals-ו ()finalizeהזכרנו את המתודות

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

(.()coutשראינו דרסנו את המתודה

חלקה שיורשת ממחלקה או מממשת ממשק, שתהה בעלת על מנת לדרוס מתודה קיימת, עלינו להגדיר מתודה חדשה במ

אותה החתימה בדיוק כמו המתודה שאנו מעוניינים להחליף את הגדרתה ובלבד שהמתודה אותה מעוניינים לדרוס אינה

מתודה סופית )יוסבר בהמשך(.

נראה דוגמה:

public class BaseClass { protected int getID(int number) { return number*number+3; } }

public class DerivedClass extends BaseClass {

protected int getID(int number) {

return number+3; } }

במחלקה היורשת, היות והחתימה של המתודה זהה לחלוטין )כאמור, תנאי הכרחי( ורק הלוגיקה getIDבאופן שבו הוגדרה

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

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

רק שההגדרה שלה הייתה זו שהגיעה ממחלקת הבסיס.

super

ולהסביר לגבי השימוש בה בהקשר של דריסת מתודות. superבנקודה זו, נכון להזכיר את המילה השמורה

. שימוש בה, בצירוף superכאשר אנו נמצאים בתחום הגדרתה של מחלקה יורשת, הופכת נגישה בפנינו המילה השמורה

נקודה מפרידה אחריה יחשוף בפנינו את משתני ומתודות מחלקת הבסיס אותה המחלקה שלנו מרחיבה.

אם לא נחשוב על כך לעומק, נוכל כמובן לחשוב ששימוש במילה שמורה זו הוא מיותר, שהרי אם המחלקה שאנו מגדירים

, והדבר אכן יהיה superשת למשתנים ומתודות שלה גם בלי המילה כרגע מרחיבה את מחלקת הבסיס, ידוע שאנו יכולים לג

נכון בכל המקרים מלבד במקרה אחד: כאשר מדובר במתודה שאת הגדרתה דרסנו ע"י מתודה חדשה.

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

לקת הבסיס הופכת להיות מוסתרת ע"י המתודה הדורסת.הגישה למתודה במח

Page 3: ג'אווה - תכנות מונחה עצמים - מתודות - דריסה/חפיפה, העמסה ומתודות בונות

ולהשתמש בה כך: superבמידה ובכל זאת נרצה לעשות שימוש במתודה המקורית, נוכל אז לשלוף את המילה השמורה

public class Human { public void cout() { System.out.println("I'm a Human!"); } }

public class Person extends Human { public void cout() { super.cout(); System.out.println("But not just a Human, I'm a person too"); } }

, נקבל את שורת הפלט:Humanעל אובייקט של ()coutבאופן זה, כאשר נפעיל את המתודה

I'm a Human!

, נקבל את שורות הפלט:Personעל אובייקט של ()coutוכאשר נפעיל את המתודה

I'm a Human!

But not just a Human, I'm a person too

:Humanשל ()coutהאחרונות נובעת מעצם הפעלת 2-שורת הפלט הראשונה מבין ה

super.cout();

:Person-ב ()coutושורת הפלט השנייה נובעות ישירות משאר הגדרתה של

System.out.println("But not just a Human, I'm a person too");

, נזכיר אותם בהמשך.superישנם עוד שימושים למילה השמורה

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

מתודות: העמסת

Page 4: ג'אווה - תכנות מונחה עצמים - מתודות - דריסה/חפיפה, העמסה ומתודות בונות

Method Overloading –חפיפת מתודות -העמסת

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

לנו את היכולת הזו הוא שמירה על שוני בחתימות המתודות החופפות.

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

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

הפרמטרים שמקבלת המתודה!

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

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

מקבלת, אך אופציה זו לא מומלצת.

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

public class ClassName { public int getID(int number, char number2) { return number*number+3; } public void getID(char number) { //Change in the number of the parameters } public int getID(int number) { //Change in the type of the parameter return number*number+3; } public void getID(int number) { //Not enough changing only the return type } public int getID(char number2, int number) { //Not Recommended changing only d order return number*number+3; } }

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

הפוכות לחלוטין האחת מהשנייה:

על מנת לדרוס מתודה, עלינו לשמור על חתימת המתודה המקורית, כך שחתימת המתודה הדורסת תהה זהה לה לחלוטין

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

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

מועמסת מזו שלידה, כלומר, ניתן להעמיס כל מתודה מס' פעמים, כל עוד יש שוני מהותי בין החתימה שלה לבין חתימות

המתודות האחרות שהועמסו.

Page 5: ג'אווה - תכנות מונחה עצמים - מתודות - דריסה/חפיפה, העמסה ומתודות בונות

Constructors -מתודות בונות

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

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

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

אותה מתודה בונה דפולטיבית, גם אם לא תוגדר על ידינו, תוגדר באופן אוטומטי וזאת בתנאי שלא הוגדרה על ידינו אף

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

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

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

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

פרמטרים:תוגדר המתודה הבונה ללא ה ClassNameלדוגמה, עבור המחלקה

public ClassName() { }

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

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

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

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

, ובהתאם 4-ל 0 במחלקה שלנו ארבעה משתנים, ואנו מעוניינים לאפשר למשתמש לשלוח לנו כל מס' של פרמטרים בין

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

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

ערכים שאנו מעוניינים להציב המתודה הבונה למתודה בונה אחרת, כשבדרך אנו נעטר ונשלים את התמונה, בהתאם ל

כברירת מחדל.

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

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

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

this(int a, int b, 1, 1);

.קריאה שכזו חייבת להיעשות בשורה הראשונה של המתודה הבונה הקוראת

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

קטע הקוד בבנאי בו התבצעה הקריאה.

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

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

המחלקה היורשת.

, הוא יופעל ראשון, ואחריו, Objectהמחלקה כך גם אם נעלה בהיררכיה לעוד מחלקות מורישות, לבסוף נגיע לבנאי של

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

Page 6: ג'אווה - תכנות מונחה עצמים - מתודות - דריסה/חפיפה, העמסה ומתודות בונות

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

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

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

BaseClass() { }

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

super

ולהסביר לגבי השימוש בה בהקשר של מתודות בונות. superבנקודה זו, נכון להזכיר שוב את המילה השמורה

. superקריאה לבנאי של מחלקת הבסיס תתבצע ע"י שימוש במילה השמורה

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

super(3,"to",6);

בדרך זו תתבצע קריאה לבנאי:

BaseClass(int a, String b, int c) { }

וכמובן שהוא צריך להיות מוגדר בה, כדי שקריאה זו תתאפשר.

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

super();

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

.הראשונה של המתודה הבונה הקוראת קריאה שכזו חייבת להיעשות בשורה

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

, חייבות שתיהן superוגם קריאה למתודה בונה ממחלקת הבסיס תוך שימוש במילה השמורה this במילה השמורה

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

השאלה, האם אין כאן סתירה בדרישות השפה?

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

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

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

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

לשם קריאה לבנאי אחר מתוך הגדרתו של בנאי? אם this -ים בראשית נשאל את עצמנו, מהי הסיבה בגינה אנו משתמש

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

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

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

ובכך פתרנו את super-בהגדרתו ולכן הוא יהיה זה שיקרא ל this-משוכלל פחות, ועל כן, אין באמת צורך בשימוש ב

הבעיה.

Page 7: ג'אווה - תכנות מונחה עצמים - מתודות - דריסה/חפיפה, העמסה ומתודות בונות

נראה מימוש להסבר הנ"ל:

public class BaseClass { BaseClass() { } }

public class DerivedClass extends BaseClass { DerivedClass() { this(0,0,0); } DerivedClass(int i) { this(i,0,0); } DerivedClass(int i, int j) { this(i,j,0); } DerivedClass(int i, int j, int k) { super(); } }