יציקת סוג אובייקט ב- Java

1. סקירה כללית

מערכת מסוג Java מורכבת משני סוגים של סוגים: ראשוניים והפניות.

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

2. פרימיטיבי לעומת הפניה

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

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

כפול myDouble = 1.1; int myInt = (int) myDouble; assertNotEquals (myDouble, myInt);

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

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

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

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

3. upcasting

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

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

כדי להדגים נידוי בואו נגדיר בעל חיים מעמד:

מעמד ציבורי בעלי חיים {public void eat () {// ...}}

עכשיו בואו נאריך בעל חיים:

מעמד ציבורי חתול מרחיב בעלי חיים {public void eat () {// ...} public void meow () {// ...}}

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

חתול חתול = חתול חדש ();

ונוכל גם להקצות אותו למשתנה הייחוס של הסוג בעל חיים:

חיה מהחי = חתול;

במשימה הנ"ל מתבצעת העלאת פנים מרומזת. נוכל לעשות זאת במפורש:

חיה = (חיה) חתול;

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

שים לב, הפניה יכולה להתייחס לכל תת-סוג מהסוג המוצהר.

באמצעות עדכונים הגבלנו את מספר השיטות העומדות בפני חתול למשל אך לא שינו את המופע עצמו. עכשיו אנחנו לא יכולים לעשות שום דבר שהוא ספציפי חתול - אנחנו לא יכולים להפעיל מיאו() על בעל חיים מִשְׁתַנֶה.

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

// animal.meow (); השיטה מיאו () אינה מוגדרת עבור סוג בעלי חיים

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

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

3.1. רב צורתיות

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

מעמד ציבורי כלב מרחיב בעלי חיים {Public void eat () {// ...}}

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

מחלקה ציבורית AnimalFeeder {Feed void public (רשימת בעלי חיים) {animal.forEach (חיה -> {animal.eat ();}); }}

אנחנו לא רוצים AnimalFeeder לדאוג לאיזה בעל חיים נמצא ברשימה - א חתול או א כֶּלֶב. בתוך ה הזנה() שיטה כולם בעלי חיים.

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

רשימת בעלי חיים = ArrayList חדש (); animal.add (חתול חדש ()); animal.add (כלב חדש ()); AnimalFeeder חדש (). להאכיל (בעלי חיים);

אנו מוסיפים חתולים וכלבים והם נדהמים ל בעל חיים הקלד באופן מרומז. כל אחד חתול הוא בעל חיים וכל אחד כֶּלֶב הוא בעל חיים. הם פולימורפיים.

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

אובייקט אובייקט = חיה חדשה ();

לכן יש לכל אובייקטים של Java שאנחנו יוצרים כבר לְהִתְנַגֵד שיטות ספציפיות, למשל, toString ().

עדנות לממשק נפוצה גם כן.

אנחנו יכולים ליצור מיילל ממשק ולעשות חתול יישם זאת:

ממשק ציבורי Mew {public void meow (); } מעמד ציבורי חתול מרחיב מכשירי בעלי חיים מיו {public void eat () {// ...} public void meow () {// ...}}

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

מיו מיו = חתול חדש ();

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

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

3.2. שׁוֹלֵט

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

הזנת חלל ציבורית (רשימת בעלי חיים) {animal.forEach (חיה -> {animal.eat ();}); }

אם נוסיף כניסה לכיתות שלנו, נראה זאת חתולשל ו כֶּלֶבהשיטות נקראות:

רשת - 15-02-2018 22: 48: 49,354 [ראשי] INFO com.baeldung.casting.Cat - החתול אוכל רשת - 2018-02-15 22: 48: 49,363 [main] INFO com.baeldung.casting.Dog - כלב אוכל 

לסיכום:

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

4. ירידה במורד

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

ניקח דוגמא:

חיה מהחי = חתול חדש ();

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

להתקשר מיאו() אנחנו צריכים להפיל בעל חיים ל חתול:

((חתול) חיה) .מיאו ();

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

בואו נכתוב את הקודם AnimalFeeder דוגמא עם מיאו() שיטה:

מחלקה ציבורית AnimalFeeder {Feed void public (רשימת בעלי חיים) {animal.forEach (חיה -> {animal.eat (); אם (חיה למשל של חתול) {((Cat) חיה). }}

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

רשת - 2018-02-16 18: 13: 45,445 [main] INFO com.baeldung.casting.Cat - החתול אוכל רשת - 2018-02-16 18: 13: 45,454 [main] INFO com.baeldung.casting.Cat - רשת מיאו - 16-02-2018 18: 13: 45,455 [ראשי] INFO com.baeldung.casting.Dog - כלב אוכל

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

4.1. מופע של מַפעִיל

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

אם (מופע בעל חיים של חתול) {((חתול) בעל חיים) .מיאו (); }

4.2. ClassCastException

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

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

חלל ציבורי uncheckedFeed (רשימת בעלי חיים) {animal.forEach (animal -> {animal.eat (); ((Cat) animal) .meow ();}); }

קוד זה אוסף ללא בעיות. אבל אם ננסה להפעיל אותו נראה חריג:

java.lang.ClassCastException: com.baeldung.casting.Dog לא ניתן ללהק אליו com.baeldung.casting.Cat

המשמעות היא שאנחנו מנסים להמיר אובייקט שהוא מופע של כֶּלֶב לתוך חתול למשל.

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

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

חיה מן החי; מחרוזת s = (מחרוזת) חיה;

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

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

בואו נסכם:

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

5. ללהק() שיטה

יש דרך נוספת להטיל חפצים בשיטות מעמד:

חלל ציבורי כאשרDowncastToCatWithCastMethod_thenMeowIsCalled () {חיה של בעלי חיים = חתול חדש (); אם (Cat.class.isInstance (חיה)) {Cat cat = Cat.class.cast (חיה); cat.meow (); }}

בדוגמה שלעיל, ללהק() ו isInstance () משתמשים בשיטות במקום יצוק ו מופע של בהתאמה.

זה נפוץ לשימוש ללהק() ו isInstance () שיטות עם סוגים גנריים.

בואו ניצור AnimalFeederGeneric כיתה עם הזנה() שיטה אשר "מאכילה" רק סוג אחד של בעלי חיים - חתולים או כלבים, בהתאם לערך של פרמטר הסוג:

מחלקה ציבורית AnimalFeederGeneric {סוג מחלקה פרטית; AnimalFeederGeneric ציבורי (סוג מחלקה) {this.type = type; } עדכון רשימות ציבורי (רשימת בעלי חיים) {רשימת רשימה = ArrayList חדש (); animals.forEach (חיה -> {אם (type.isInstance (חיה)) {T objAsType = type.cast (חיה); list.add (objAsType);}}); רשימת החזרה; }}

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

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

בואו נעשה ט שווה ל חתול וודא שהשיטה מחזירה רק חתולים:

@ מבחן פומבי בטל כאשר ParameterCat_thenOnlyCatsFed () {רשימת בעלי חיים = ArrayList חדש (); animal.add (חתול חדש ()); animal.add (כלב חדש ()); AnimalFeederGeneric catFeeder = חדש AnimalFeederGeneric (Cat.class); רשימת fedAnimals = catFeeder.feed (בעלי חיים); assertTrue (fedAnimals.size () == 1); assertTrue (fedAnimals.get (0) מופע של חתול); }

6. מסקנה

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

כמו תמיד, הקוד של מאמר זה זמין באתר GitHub.


$config[zx-auto] not found$config[zx-overlay] not found