java - בעיה עם ערך מוחזר מ-instanceof וירושה - תכנות - HWzone פורומים
עבור לתוכן
  • צור חשבון

java - בעיה עם ערך מוחזר מ-instanceof וירושה


LennonRulz

Recommended Posts

שלום לכולם,

יש לי בעיה אמיתית אני אתן דוגמה:

יש לי מחלקה אבסטרקטית A.

מתחתיה יש לי מחלקה רגילה B שיורשת אותה.

לצידה של מחלקה B יש לי מחלקה אבסטרקטית C שיורשת גם היא את מחלקה A, ומתחת ל-C יש לי מחלקה רגילה D שיורשת את C.

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

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

במחלקה check בפונק' הראשית

public static void main(String[]  args)

אני יוצר משתנה(=אובייקט, עצם) מסוג B בשם objB ומשתנה מסוג D בשם objD.

אח"כ אני כותב את התנאי

if(objB instanceof C)

.

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


inconvertible types
required: D; found: B

אוקיי עכשיו לבעיה יותר מהותית:

בנוסף להכל, יש לי עוד מחלקה רגילה שיורשת את A, נגיד sisterB.

גם מתחת ל-C יש לי עוד מחלקה רגילה שיורשת אותה, נגיד sisterD.

למחלקה C יש תכונה- איזשהו מספר savedNum מסוג int בהרשאת protected(כלומר שלכל המחלקות שיורשות את המחלקה C יש גישה אליו) שמייצג קצה של איזשהו טווח מספרים(שאותו כל המחלקות, כולל יורשות של A, יודעות כבר) אבל הוא משתנה כל הזמן ע"פ תנאי שתכף אגיד(זה מה שייחודי במחלקה C).

למחלקה C יש פונק' get ו-set ל-savedNum הזה. שניהן בהרשאת public ככה שבוודאות לכל המחלקות שיורשות את מחלקה C יש גישה לפונק' האלה.

לכל אחת מהמחלקות B, sisterB, D ו-sisterD יש פונק' שנקראת giveNum, שמגרילה מספר מסוג int בטווח המספרים הידוע הזה ומחזירה אותו(בפועל בספרייה שלי הטכניקה קצת שונה אבל פה זה לצורך העניין).

אני יוצר מערך Aים בשם arr שכל תא בו הוא אובייקט מסוג B, sisterB, D או sisterD וכותב את הקוד הבא:


int numFromUser;
for(int i=0; i<arr.length; i++)
{
numFromUser=arr[i].giveNum();
for(int j=0; j<arr.length; j++)
{
if(arr[j] instanceof C)
{
if(numFromUser<((C)arr[j]).getSavedNum())
{
arr[j].setSavedNum(numFromUser);
}
}
}
}

אוקיי חבר'ה תודה על הסבלנות, עכשיו לבעיה-

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

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

כשהמשתנה numFromUser מקבל ערך מהפונקציה giveNum() באחת המחלקות שיורשות את A חוץ מ-C(המחלקות B ו-sisterB), בזמן הריצה אני מקבל את השגיאה


java.lang.ClassCastException: B cannot be cast to C
at check.main(check.java:|Line number|)

.

יכול להיות גם sisterB במקום. ה-Line number זה המספר שורה של התנאי השני והמהדר מצביע עליה.

אם הערך מתקבל מהפונקציה giveNum() באחת המחלקת D או sisterD הכל בסדר והפעולה בתוך התנאי השני מתקיימת.

תודה, תומר.

קישור לתוכן
שתף באתרים אחרים

לגבי הבעיה הראשונה -

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

לעומת זאת, אם תגדיר:

A objB = new B();

השורה

objB instanceof C

תתקמפל כראוי, מכיוון שאובייקט מסוג A *יכול* להיות C

- - - תגובה אוחדה: - - -

לגבי השאלה השניה, אני לא רואה בעיה מיידית (חוץ מזה שבשורה שקוראת ל-set אין casting אז לא יכול להיות שהקוד שלך מתקמפל ככה סתם...)

אם תוכל להראות את ההכרזה והאתחול של המערך זה יעזור למצוא מה הבעיה

קישור לתוכן
שתף באתרים אחרים

לגבי הבעיה הראשונה -

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

לעומת זאת, אם תגדיר:

A objB = new B();

השורה

objB instanceof C

תתקמפל כראוי, מכיוון שאובייקט מסוג A *יכול* להיות C

- - - תגובה אוחדה: - - -

לגבי השאלה השניה, אני לא רואה בעיה מיידית (חוץ מזה שבשורה שקוראת ל-set אין casting אז לא יכול להיות שהקוד שלך מתקמפל ככה סתם...)

אם תוכל להראות את ההכרזה והאתחול של המערך זה יעזור למצוא מה הבעיה

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

וצודק עשיתי casting פשוט שכחתי לרשום בהודעה.

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

אח"כ עברתי על המערך תא-תא וביקשתי ממנו שיכניס מספר שייצג סוג אובייקט- 0 עבור B, אחד עבור sisterB וכן הלאה...


import java.util.*;
.
.
.
.
static Scanner reader=new Scanner(System.in);
.
.
.
.
System.out.println("Enter number: ");
int n=reader.nextInt();
int type;
A[] arr=new A[n];
for(int i=0; i<arr.length; i++)
{
System.out.println("Enter type: ");
switch(type)
{
case 0: new B(); break;
case 1: new sisterB(); break;
case 2: new D(); break;
case 3: new sisterD(); break;
}
}

קישור לתוכן
שתף באתרים אחרים

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

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

קישור לתוכן
שתף באתרים אחרים

אם כל האובייקטים במערך הם בנים של C אז למה אתה צריך לעשות CAST שאתה משתמש בפונקציה?

אתה אמור להבין מהשגיאה (ומהקוד שלך) שאין לך אפשרות לעשות CAST מB לC כי הם אחים

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

קישור לתוכן
שתף באתרים אחרים

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

זו בעצם המהות של פולימורפיזם וממשק.

בדיוק כמו שאתה יכול לכתוב פונקציה שמקבלת List. אתה לא יודע באיזה List מדובר מאחר ו-List זה ממשק - אתה יודע שאתה ניגש לאיבר עם get למשל וכולי. אתה לא יודע אם זה LinkedList או ArrayList וגם לא מעניין אותך.

קישור לתוכן
שתף באתרים אחרים

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

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

האמת שזה קוד קצת גדול, אני מנסה לפתור את התרגיל במדריך הזה https://he.wikibooks.org/wiki/%D7%AA%D7%9B%D7%A0%D7%95%D7%AA_%D7%9E%D7%AA%D7%A7%D7%93%D7%9D_%D7%91-Java/%D7%A4%D7%95%D7%9C%D7%99%D7%9E%D7%95%D7%A8%D7%A4%D7%99%D7%96%D7%9D/%D7%AA%D7%A8%D7%92%D7%99%D7%9C%D7%99%D7%9D

התחלתי ללמוד את הנושא לפני כמה ימים.

בכל מקרה הנה כל המחלקות:

מחלקה של המשחק: http://pastebin.com/y0G7kLRK

מחלקה של שחקן באופן כללי: http://pastebin.com/i2YtpNqd

מחלקה של שחקן מסוג בן אדם: http://pastebin.com/x8mdUGH6

מחלקה של שחקן מסוג קוף: http://pastebin.com/TfMrZuew

מחלקה של שחקן אינטליגנטי(שמסוגל לזכור טווח מספרים שהמספר נמצא בו, משהו שלא נמצא בתרגיל): http://pastebin.com/4gCD4rcX

מחלקה של מחשב מהמר: http://pastebin.com/DgkdEAyZ

מחלקה של מחשב חכם: http://pastebin.com/VfvJXFV2

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


java.lang.ClassCastException: Monkey cannot be cast to intelPlayer
at Game.main(Game.java:93)


.

הדבר הזה לא קורה כששחקן מסוג מחשב חכם או מחשב מהמר מנחש מספר.

אם כל האובייקטים במערך הם בנים של C אז למה אתה צריך לעשות CAST שאתה משתמש בפונקציה?

אתה אמור להבין מהשגיאה (ומהקוד שלך) שאין לך אפשרות לעשות CAST מB לC כי הם אחים

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

כל האובייקטים במערך הם בנים/נכדים/צאצא כל שהוא של A לא של C.

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

בכל מקרה, בהתחלה גם אני ניסיתי להפעיל את הפונקציה getSavedNum() בלי לעשות cast אבל עדיין קיבלתי הודעה שהפונק' לא נמצאה במחלקה(למרות ש-D ו-sisterD בוודאות יורשים את C) וזה היה לי מוזר, אז בסוף קצת שיחקתי עם זה עד שגיליתי שזה עובד עם cast(דרך אגב עד עכשיו אני לא מבין למה אם תוכל להסביר לי אשמח).

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

זו בעצם המהות של פולימורפיזם וממשק.

בדיוק כמו שאתה יכול לכתוב פונקציה שמקבלת List. אתה לא יודע באיזה List מדובר מאחר ו-List זה ממשק - אתה יודע שאתה ניגש לאיבר עם get למשל וכולי. אתה לא יודע אם זה LinkedList או ArrayList וגם לא מעניין אותך.

רגע אבל פה אני בפירוש רשמתי new A[]. זה שזה מערך אומר שזה לא מופע של A?

קישור לתוכן
שתף באתרים אחרים

תבדוק את האינדקסים בלולאה הפנימית :)

אתה בסדר עם הקאסטינג

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

רגע אבל יש לי עכשיו שאלה אחרת שגם שאלתי קודם- למה בעצם זה מכריח אותי לעשות קאסטינג? אם אני עובר על המערך ומגיע לשחקן מסוג מחשב חכם או מחשב מהמר שיורשים את המחלקה של שחקן אינטליגנטי, למה אני לא יכול לעשות פשוט לעשות player[j].setTop(playerGuess)?

קישור לתוכן
שתף באתרים אחרים

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

האמת למדתי, כל יום לומדים משהו חדש.

אבל בנוגע לקטע עם ה-cast, למה זה הכרחי? עדיין לא הבנתי כ"כ.

קישור לתוכן
שתף באתרים אחרים

כי הקומפיילר לא יודע ש-[player[j הוא מסוג IntelPlayer ושיש לו פונקציה בשם setTop. בזמן הקומפילציה הטיפוס של [player[j הוא רק Player.

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

יש לי אבל שאלה אחרת שעלתה בינתיים לא קשורה לירושה-

שיניתי קצת את המחלקה גיים: http://pastebin.com/UZSmeYe7(שורה 92 ודומותיה). במקום לכתוב נגיד "Human instance of intelPlayer=false" זה כותב "Human@46a0e13e instanceof intelPlayer=false", ובמקום נגיד "GamblingComputer instanceof intelPlayer=true" זה כותב "GamblingComputer@239a6904 instanceof intelPlayer=true". הקטע שאחרי שם המחלקה(סימן @ והרצף מספרים ואותיות הזה) נשאר זהה עבור כל שחקן גם בניחושים של שאר השחקנים. מישהו יודע למה?

ונגיד אני מכניס בשם של שחקן מסוים "יוסי" וזה אמור להדפיס:"שלום יוסי!......", זה מדפיס במקום "יוסי" ג'יבריש.

אולי פשוט משהו במהדר עצמו(משתמש בבלו-ג'יי).

תודה.

קישור לתוכן
שתף באתרים אחרים

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

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

reader = new Scanner(in, "UTF-8");

קישור לתוכן
שתף באתרים אחרים

ארכיון

דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.

×
  • צור חדש...