JavaRush /בלוג Java /Random-HE /הרחבה וכיווץ של סוגי התייחסות

הרחבה וכיווץ של סוגי התייחסות

פורסם בקבוצה
שלום! באחת ההרצאות הקודמות דנו בליהוק טיפוסים פרימיטיביים. בואו נזכור בקצרה על מה דיברנו. הרחבה וכיווץ של סוגי התייחסות - 1ייצגנו טיפוסים פרימיטיביים (במקרה זה, מספרי) כבובות קינון לפי כמות הזיכרון שהן תופסות. כפי שאתה זוכר, הנחת בובת קינון קטנה יותר לתוך בובת גדולה יותר תהיה פשוטה הן בחיים האמיתיים והן בתכנות Java.
public class Main {
   public static void main(String[] args) {
        short smallNumber = 100;
        int bigNumber =  smallNumber;
        System.out.println(bigNumber);
   }
}
זוהי דוגמה להמרה אוטומטית, או הרחבה . זה קורה מעצמו, אז אין צורך לכתוב קוד נוסף. בסופו של דבר, אנחנו לא עושים שום דבר חריג: אנחנו פשוט מכניסים בובת קינון קטנה יותר לבובת קינון גדולה יותר. זה עניין אחר אם ננסה לעשות את ההפך ולהכניס בובת מטריושקה גדולה לקטנה יותר. אי אפשר לעשות את זה בחיים, אבל בתכנות אפשר לעשות את זה. אבל יש אזהרה אחת. אם ננסה להכניס ערך intלמשתנה short, זה לא יסתדר כל כך בקלות. אחרי הכל, רק 16 סיביות של מידע יכולות להתאים למשתנה short, אבל הערך intלוקח 32 סיביות! כתוצאה מכך, הערך המשודר יתעוות. המהדר ייתן לנו שגיאה (" אחי, אתה עושה משהו חשוד! "), אבל אם נציין במפורש לאיזה סוג אנחנו משליכים את הערך שלנו, הוא עדיין יבצע פעולה כזו.
public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

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

דוגמאות להרחבה וכיווץ של סוגי התייחסות

כעת נדבר על אותן פעולות, אך ישימות לא על טיפוסים פרימיטיביים, אלא על אובייקטים ומשתני התייחסות ! איך זה עובד בג'אווה? די פשוט למעשה. יש חפצים שאינם קשורים זה לזה. זה יהיה הגיוני להניח שלא ניתן להמיר אותם זה לזה במפורש או אוטומטית:
public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Dog();//error!

   }

}
כאן נקבל, כמובן, שגיאה. השיעורים Catאינם Dogקשורים זה לזה, ולא כתבנו "ממיר" מאחד לשני. זה הגיוני שלא נוכל לעשות זאת: למהדר אין מושג איך להמיר את האובייקטים הללו ביניהם. זה עניין אחר אם החפצים מחוברים זה לזה! אֵיך? קודם כל, שימוש בירושה. בואו ננסה ליצור מערכת כיתות קטנה עם ירושה. תהיה לנו שיעור כללי המייצג בעלי חיים:
public class Animal {

   public void introduce() {

       System.out.println("i'm Animal");
   }
}
חיות, כידוע, הן ביתיות ופראיות:
public class WildAnimal extends Animal {

   public void introduce() {

       System.out.println("i'm WildAnimal");
   }
}

public class Pet extends Animal {

   public void introduce() {

       System.out.println("i'm Pet");
   }
}
לדוגמא, ניקח כלבים - כלב בית וזאב ערבות:
public class Dog extends Pet {

   public void introduce() {

       System.out.println("i'm Dog");
   }
}





public class Coyote extends WildAnimal {

   public void introduce() {

       System.out.println("i'm Coyote");
   }
}
השיעורים שלנו הם בכוונה הפרימיטיביים ביותר כדי שיהיה קל יותר לתפיסתם. אנחנו לא באמת צריכים שדות כאן, ודי בשיטה אחת. בואו ננסה להפעיל את הקוד הבא:
public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
מה לדעתך ייצא לקונסולה? introduceהאם שיטת הכיתה Petאו הכיתה תעבוד Animal? נסה לנמק את תשובתך לפני שתמשיך לקרוא. והנה התוצאה! אני חיית מחמד למה התשובה יצאה כך? זה פשוט. יש לנו משתנה אב ואובייקט ילד. בכתיבה:
Animal animal = new Pet();
הרחבנו סוג הפניהPet ואחסנו את האובייקט שלו במשתנה Animal. בדומה לסוגים פרימיטיביים, הרחבת סוגי הפניות ב-Java מתבצעת באופן אוטומטי. אין צורך לכתוב קוד נוסף בשביל זה. כעת יש לנו אובייקט ילד המצורף להפניה להורה, וכתוצאה מכך אנו רואים שהשיטה נקראת בכיתה הילד. אם אתה עדיין לא מבין לגמרי למה הקוד הזה עובד, כתוב אותו מחדש בשפה פשוטה:
Животное животное = new ДомашнееЖивотное();
אין שום בעיה עם זה, נכון? תאר לעצמך שזה החיים האמיתיים, והקישור במקרה הזה הוא תג נייר פשוט שכתוב עליו "חיה". אם תיקח פיסת נייר כזו ותחבר אותה לצווארון של כל חיית מחמד, הכל יהיה בסדר. כל חיית מחמד היא עדיין חיה! התהליך ההפוך, כלומר ירידה בעץ הירושה ליורשים, הוא צמצום:
public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
כפי שאתה יכול לראות, כאן אנו מציינים במפורש לאיזה מחלקה אנו רוצים להטיל את האובייקט שלנו. בעבר היה לנו משתנה WildAnimal, ועכשיו Coyote, שיורד בעץ הירושה. זה הגיוני שהמהדר לא ידלג על פעולה כזו ללא אינדיקציה מפורשת, אבל אם תציין את הסוג בסוגריים הכל יעבוד. הרחבה וכיווץ של סוגי התייחסות - 2 בואו נסתכל על דוגמה נוספת, מעניינת יותר:
public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal();//error!
   }
}
המהדר זורק שגיאה! מה הסיבה? העובדה היא שאתה מנסה להקצות אובייקט אב למשתנה צאצא. במילים אחרות, אתה רוצה לעשות את זה:
ДомашнееЖивотное домашнееЖивотное = new Животное();
אבל אולי אם נציין במפורש את הטיפוס אליו אנחנו מנסים ללהק, נצליח? נראה שהמספרים עובדים, בואו ננסה את זה! :)
public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
חריגה בשרשור "ראשי" java.lang.ClassCastException: לא ניתן להטיל חיה ל-Pet Error! המהדר לא התלונן הפעם, אך כתוצאה מכך קיבלנו חריגה. אנחנו כבר יודעים את הסיבה: אנחנו מנסים להקצות אובייקט אב למשתנה צאצא. למה, בעצם, אי אפשר לעשות את זה? כי לא כל החיות הן חיות מחמד. יצרת אובייקט Animalואתה מנסה להקצות אותו למשתנה Pet. אבל, למשל, זאב ערבות הוא גם Animal, אבל הוא לא Pet, חיית בית. במילים אחרות, כשאתה כותב:
Pet pet = (Pet) new Animal();
new Animal()כל חיה יכולה להיות שם , והיא לא חייבת להיות ביתית! מטבע הדברים, המשתנה שלך Pet petמתאים רק לאחסון חיות מחמד (וצאצאיהם), ולא לכולם. לכן, עבור מקרים כאלה, נוצר חריג מיוחד ב-Java - ClassCastExceptionשגיאה בעת יציקת מחלקות. בוא נגיד את זה שוב כדי להבהיר את זה. משתנה אב (הפניה) יכול להצביע על אובייקט של מחלקה צאצאית:
public class Main {

   public static void main(String[] args) {

       Pet pet =  new Pet();
       Animal animal = pet;

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
לדוגמה, לא יהיו לנו בעיות כאן. יש לנו אובייקט Petשאליו מצביע קישור Pet. ואז החל קישור חדש להצביע על אותו אובייקט Animal. לאחר מכן אנו מבצעים את ההמרה animalל Pet. למה עשינו את זה, דרך אגב? בפעם האחרונה קיבלנו חריגה! כי הפעם החפץ המקורי שלנו הוא Pet pet!
Pet pet =  new Pet();
ובדוגמה הקודמת זה היה אובייקט Animal:
Pet pet = (Pet) new Animal();
לא ניתן להקצות אובייקט קדמון למשתנה צאצא. להיפך, אתה יכול לעשות את זה.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION