JavaRush /จาวาบล็อก /Random-TH /การขยายและการย่อประเภทอ้างอิง

การขยายและการย่อประเภทอ้างอิง

เผยแพร่ในกลุ่ม
สวัสดี! ในการบรรยายครั้งก่อน เราได้พูดคุยถึงการคัดเลือกนักแสดงประเภทดั้งเดิม จำสั้น ๆ ว่าเราพูดถึงอะไร การขยายและการหดตัวของประเภทอ้างอิง - 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);

   }

}
ในตัวอย่างข้างต้น เราทำอย่างนั้น การดำเนินการเสร็จสมบูรณ์ แต่เนื่องจากshortมีเพียง 16 บิตจาก 32 บิตเท่านั้นที่พอดีกับตัวแปร ค่าสุดท้ายจึงบิดเบี้ยว และด้วยเหตุนี้ เราจึงได้รับหมายเลข-27008 การดำเนินการนี้เรียกว่าการแปลงอย่างชัดเจน หรือการจำกัดให้แคบลง

ตัวอย่างการขยายและการย่อประเภทอ้างอิง

ตอนนี้เราจะพูดถึงการดำเนินการเดียวกัน แต่ไม่สามารถใช้ได้กับประเภทดั้งเดิม แต่ใช้กับอ็อบเจ็กต์และตัวแปรอ้างอิง ! สิ่งนี้ทำงานอย่างไรใน Java? ค่อนข้างง่ายจริงๆ มีวัตถุที่ไม่เกี่ยวข้องกัน มีเหตุผลที่จะถือว่าไม่สามารถแปลงเป็นกันโดยตรงหรือโดยอัตโนมัติ:
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();
   }
}
ข้อยกเว้นในเธรด "main" 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();
และในตัวอย่างก่อนหน้านี้มันคือ object Animal:
Pet pet = (Pet) new Animal();
ไม่สามารถกำหนดตัวแปรสืบทอดให้กับออบเจ็กต์ระดับบนได้ ในทางตรงกันข้าม คุณก็สามารถทำได้
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION