CodeGym /จาวาบล็อก /สุ่ม /การขยายและการจำกัดประเภทการอ้างอิงให้แคบลง
John Squirrels
ระดับ
San Francisco

การขยายและการจำกัดประเภทการอ้างอิงให้แคบลง

เผยแพร่ในกลุ่ม
สวัสดี! ในบทเรียนที่ผ่านมา เราได้พูดถึงการหล่อประเภทดั้งเดิม ลองนึกถึงสิ่งที่คุยกันสั้น ๆ การขยายและการจำกัดประเภทการอ้างอิงให้แคบลง - 1เรานึกภาพประเภทดั้งเดิม (ในกรณีนี้คือประเภทตัวเลข) เป็นตุ๊กตาที่ทำรังซึ่งมีขนาดแตกต่างกันไปตามจำนวนหน่วยความจำที่ใช้ อย่างที่คุณคงจำได้ การใส่ตุ๊กตาตัวเล็กลงในตุ๊กตาตัวใหญ่นั้นง่ายมากทั้งในชีวิตจริงและในการเขียนโปรแกรม Java

public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
นี่คือตัวอย่างของการแปลงหรือขยาย โดย อัตโนมัติ มันเกิดขึ้นเอง คุณจึงไม่ต้องเขียนโค้ดเพิ่มเติม ในท้ายที่สุด เราไม่ได้ทำอะไรที่ผิดปกติ เราแค่ใส่ตุ๊กตาตัวเล็กลงในตุ๊กตาตัวใหญ่ เป็นอีกเรื่องหนึ่งหากเราพยายามทำในสิ่งที่ตรงกันข้ามและใส่ตุ๊กตารัสเซียตัวใหญ่ลงในตุ๊กตาตัวเล็กกว่า คุณไม่สามารถทำได้ในชีวิตจริง แต่ในการเขียนโปรแกรมคุณทำได้ แต่มีความแตกต่างกันนิดหน่อย หากเราพยายามใส่ an 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);

   }

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

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

ตอนนี้เรามาพูดถึงตัวดำเนินการเดียวกันที่ไม่ได้ใช้กับประเภทดั้งเดิม แต่ใช้กับวัตถุและตัวแปรอ้างอิง ! สิ่งนี้ทำงานใน Java ได้อย่างไร จริงๆแล้วมันค่อนข้างง่าย มีวัตถุที่ไม่เกี่ยวข้อง มีเหตุผลที่จะถือว่าพวกเขาไม่สามารถแปลงซึ่งกันและกันได้ ทั้งโดยชัดแจ้งหรือโดยอัตโนมัติ:

public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

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

   }

}
แน่นอนว่าเราได้รับข้อผิดพลาดที่นี่ คลาสCatand 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ตัวแปร และตอนนี้เรามี a 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แต่ไม่ใช่Peta กล่าวอีกนัยหนึ่งเมื่อคุณเขียน

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 = new Pet();
แต่ในตัวอย่างสุดท้าย มันเป็นAnimalวัตถุ:

Pet pet = (Pet) new Animal();
คุณไม่สามารถกำหนดวัตถุบรรพบุรุษให้กับตัวแปรที่สืบทอดมา คุณสามารถทำสิ่งที่ตรงกันข้าม
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION