อธิบายคำตอบของ Michael Berry อย่างละเอียด
Dog d = (Dog)Animal; //Compiles but fails at runtime
ที่นี่คุณกำลังพูดกับคอมไพเลอร์ "เชื่อฉันฉันรู้ว่าd
มันหมายถึงDog
วัตถุ" แม้ว่ามันจะไม่ใช่
โปรดจำไว้ว่าคอมไพเลอร์ถูกบังคับให้ความไว้วางใจเราเมื่อเราทำตาละห้อย
คอมไพเลอร์เท่านั้นที่รู้เกี่ยวกับประเภทอ้างอิงที่ประกาศไว้ JVM ที่รันไทม์รู้ว่าวัตถุคืออะไร
ดังนั้นเมื่อ JVM ที่รันไทม์คิดว่าDog d
จริง ๆ แล้วเป็นการอ้างถึงวัตถุAnimal
ไม่ใช่Dog
วัตถุ เฮ้ ... ClassCastException
คุณโกหกคอมไพเลอร์และพ่นไขมันใหญ่
ดังนั้นหากคุณกำลังใจร้อนคุณควรใช้instanceof
การทดสอบเพื่อหลีกเลี่ยงการทำให้ผิดพลาด
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
ตอนนี้คำถามมาถึงใจของเรา ทำไมคอมไพเลอร์นรกจึงอนุญาตให้ downcast เมื่อในที่สุดมันจะขว้างjava.lang.ClassCastException
?
คำตอบก็คือว่าทุกคอมไพเลอร์สามารถทำได้คือการตรวจสอบว่าทั้งสองประเภทอยู่ในต้นไม้มรดกเดียวกันดังนั้นขึ้นอยู่กับสิ่งที่อาจจะมีรหัสมาก่อนที่จะเศร้าใจก็เป็นไปได้ว่าเป็นประเภทanimal
dog
คอมไพเลอร์ต้องอนุญาตสิ่งต่าง ๆ ที่อาจเป็นไปได้ในการทำงานขณะรันไทม์
พิจารณาตัวอย่างรหัสต่อไปนี้:
public static void main(String[] args)
{
Dog d = getMeAnAnimal();// ERROR: Type mismatch: cannot convert Animal to Dog
Dog d = (Dog)getMeAnAnimal(); // Downcast works fine. No ClassCastException :)
d.eat();
}
private static Animal getMeAnAnimal()
{
Animal animal = new Dog();
return animal;
}
อย่างไรก็ตามหากคอมไพเลอร์แน่ใจว่านักแสดงจะไม่สามารถทำงานได้การรวบรวมจะล้มเหลว IE ถ้าคุณพยายามที่จะโยนวัตถุในลำดับชั้นการสืบทอดที่แตกต่างกัน
String s = (String)d; // ERROR : cannot cast for Dog to String
ซึ่งแตกต่างจากการดาวน์สตรีมการอัพคาสต์ทำงานโดยปริยายเพราะเมื่อคุณอัปสตรีมคุณกำลัง จำกัด จำนวนวิธีที่คุณสามารถเรียกใช้โดยตรงกันข้ามกับการดาวน์สตรีมซึ่งหมายความว่าในภายหลังคุณอาจต้องการเรียกใช้วิธีที่เฉพาะเจาะจงมากขึ้น
Dog d = new Dog();
Animal animal1 = d; // Works fine with no explicit cast
Animal animal2 = (Animal) d; // Works fine with n explicit cast
ทั้งสอง upcast ข้างต้นจะทำงานได้ดีโดยไม่มีข้อยกเว้นเพราะ Dog IS-A Animal, anithing สัตว์สามารถทำได้สุนัขสามารถทำได้ แต่มันไม่ใช่ความจริงในทางกลับกัน