virtual ב JAVA

ehudl1

New member
virtual ב JAVA

שלום,

יש לי "אבא" שנקרא לו: כלי
ואז יש לי בנים.

אני נמצא כרגע בתוך האבא: אם הבן הוא X אני רוצה שהוא יעשה מתודה מסויימת, אם הוא Y מתודה אחרת וכו'...
איך אני ניגש למתודה של הבן?

תודה.
 

ehudl1

New member
הנה ההסבר בדוגמא:

public void searchTool (JButton[][] _buttons, String str, char s, Board d, ArrayList _a, ArrayList _a_IP)
{
b=d;
a=_a;
a_IP=_a_IP;
c=s;
buttons=_buttons;

if (str.equals("soldier"))
{
// options
checkLigal(c);
paintOptions(buttons, a_IP);
firstTime=1;
}

if (str.equals("rook"))
{
// rook options
checkLigal(c);
paintOptions(buttons, a_IP);
firstTime=1;
}

if (str.equals("horse"))
{
// horse options
checkLigal(c);
paintOptions(buttons, a_IP);
firstTime=1;
}

if (str.equals("run"))
{
// horse options
checkLigal(c);
paintOptions(buttons, a_IP);
firstTime=1;
}
}

כאשר לדוגמא חייל מוגדר ככה:

public class Soldier extends Tool


תודה על העזרה.
 

BravoMan

Active member
נראה שלא הבנת את נושא הירושה ופולימורפיזם...

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

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

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

אבל, השאלה האמתית היא, האם אתה בכלל צריך "מחלקת אב".

ב-Java אין "פונקציה ווירטואלית טהורה" כמו שיש ב-++C. אם אתה רוצה שכמה אובייקטים יתנהגו זהה ויממשו את אותן הפונקציות, אבל אין שום קוד משותף לפונקציות האלה שאפשר לשים במחלקת בסיס, כדאי להגדיר Interface במקום.

האם יש לך בכלל קוד שצריך "לחיות" במחלקת "כלי"? קוד שמשותף לכל הכלים?

ועוד עצה לדרך: הימנע משימוש במשתנים בעלי שם לא מובן, בעיקר שם של אות אחת.
זה בסדר להשתמש ב-i ו-j ללולאות, וזה הגיוני להשתמש ב-x ו-y לציון נקודה, אבל מעבר לזה כדאי לתת שמות משמעותיים יותר מ-a, b ,c וכו'
 

ehudl1

New member
תשובה

כן, יש לי כמה מתודות משותפות לכולם:

setOptions()
paintOptions()


הבנתי את כוונתך, אבל משום מה אני מקבל שגיאה נגיד כאן:


void setGame()
{
((Soldier)(t)).setOnBoard(buttons);
((Horse)(t)).setOnBoard(buttons);
((Queen)(t)).setOnBoard(buttons);
((King)(t)).setOnBoard(buttons);
((Rook)(t)).setOnBoard(buttons);
((Run)(t)).setOnBoard(buttons);
}



public void setOnBoard(JButton[][] button)
{
for (int j=0; j < SIZE; j++)
buttons[1][j].setIcon(white_soldier);

for (int j=0; j < SIZE; j++)
buttons[6][j].setIcon(black_soldier);
}


השגיאה:

Exception in thread "main" java.lang.ClassCastException: Tool cannot be cast to Soldier
at SetBoard.setGame(SetBoard.java:112)
at SetBoard.DrawBoard(SetBoard.java:101)
at SetBoard.<init>(SetBoard.java:39)
at Game.main(Game.java:15)

לא ברור לי מדוע עכשיו הוא לא מוכן לעשות קאסטינג, שהרי הוא בן של t .
(t=tool)
 

BravoMan

Active member
משום שאתה עושה Casting הפוך.

תחשוב על זה במושגים של העולם האמתי:

"משאית" היא סוג של רכב, אבל "רכב" הוא לא סוג של משאית.
"רץ" הוא סוג של כלי, אבל "כלי" הוא לא סוג של רץ.

אתה לא יכול לכפות על מחלקת אב להפוך למחלקת בן.

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

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

BravoMan

Active member
הבהרה:

(עקב השעה המאוחרת ההסבר בהודעה קודמת יצא קצת עקום)

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

רק אם בתוך t היה לך באמת אובייקט מסוג Soldier היה ה-Casting מצליח.

אבל שוב, למה אתה עושה casting???
 

ehudl1

New member
אז איך אני פונה ספציפית לכל מתודה רלוונטית?

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

ehudl1

New member
חשבתי גם לנסות ככה:

Soldier soldier;

soldier.setOnBoard(buttons);

אבל אז משום מה הוא החליט שאני "מצביע ל- null".


Exception in thread "main" java.lang.NullPointerException
at SetBoard.setGame(SetBoard.java:111)
at SetBoard.DrawBoard(SetBoard.java:100)
at SetBoard.<init>(SetBoard.java:39)
at Game.main(Game.java:15)
 

Guy Yafe

New member
אני חושב שבJAVA יש פונקציה וירטואלית טהורה

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

ehudl1

New member
זה נכון, אבל עדיין זה לא גורם לי "לפנות"

ספציפית למתודה הרצויה.
 

Guy Yafe

New member
אתה תמיד תפנה לפונקצייה הכי נמוכה בשרשרת

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

ehudl1

New member
בגלל זה חשבתי להגדיר ככה:

Soldier soldier;

soldier.setOnBoard(buttons);


אבל כמו שרשמתי כבר קודם זה לא מסתדר כי משהו שם עדיין null.


Exception in thread "main" java.lang.NullPointerException
at SetBoard.setGame(SetBoard.java:111)
at SetBoard.DrawBoard(SetBoard.java:100)
at SetBoard.<init>(SetBoard.java:39)
at Game.main(Game.java:15)

בהנחה והבנתי אותך נכון...
 

Guy Yafe

New member
ודאי שזה NULL

הצהרת על אובייקט ולא איתחלת אותו באמצעות new. אני מתפלא שזה התקמפל לך.

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

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

BravoMan

Active member
הניחוש שלי הוא שאתה רוצה בעצם פונקציה סטטית

האם פונקציה setOnBoard, אמורה למקם את כל הכלים מהסוג הנתון על הלוח במצב ההתחלתי שלהם?

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