CodeGym /בלוג Java /Random-HE /הרחבה והצרה של סוגי התייחסות
John Squirrels
רָמָה
San Francisco

הרחבה והצרה של סוגי התייחסות

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

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
זה בדיוק מה שעשינו בדוגמה למעלה. הפעולה בוצעה, אך מכיוון שהמשתנה shortיכול להכיל רק 16 מתוך 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. אתה לא צריך לכתוב קוד נוסף כדי שזה יקרה. כעת יש לנו אובייקט צאצא שהוקצה להפניית אב. כתוצאה מכך, אנו רואים שקריאת השיטה מתבצעת במחלקה הצאצאית. אם אתה עדיין לא מבין לגמרי למה הקוד הזה עובד, כתוב אותו מחדש בשפה פשוטה:
Animal animal = new DomesticatedAnimal();
אין שום בעיה עם זה, נכון? תארו לעצמכם שאלו הם החיים האמיתיים, והרפרנס הוא פשוט תווית נייר שעליה כתוב 'חיה'. אם תיקח את פיסת הנייר ותחבר אותה לצווארון של כל חיית מחמד, הכל יהיה נכון. אחרי הכל, כל חיית מחמד היא חיה! התהליך ההפוך - מעבר בעץ הירושה לצאצאים - הולך ומצטמצם:
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!
   }
}
המהדר יוצר שגיאה! אבל למה? מכיוון שאתה מנסה להקצות אובייקט אב להפניה צאצאית. במילים אחרות, אתה מנסה לעשות משהו כזה:
DomesticatedAnimal domesticatedAnimal = new Animal();
ובכן, אולי הכל יעבוד אם נציין במפורש את הסוג שאליו אנחנו מנסים להמיר? זה עבד עם מספרים - בואו ננסה את זה! :)
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מתאים רק לאחסון חיות מחמד (וצאצאיהם) ולא כל סוג של בעל חיים. לכן 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ל- a 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