マイケル・ベリーの答えを詳しく説明します。
Dog d = (Dog)Animal; //Compiles but fails at runtime
ここでは、コンパイラに「Trust me。私d
は本当にDog
オブジェクトを参照していることを知っている」と言っていますが、そうではありません。
ダウンキャストを行うとき、コンパイラーは私たちを信頼することを強いられることを覚えておいてください。
コンパイラは、宣言された参照型のみを認識します。実行時のJVMは、オブジェクトが実際に何であるかを認識しています。
したがって、実行時のJVMが、Dog d
が実際にはを参照しているのAnimal
であって、Dog
それが言うオブジェクトを参照しているのではないことを理解したとき。ねえ...あなたはコンパイラに嘘をつき、大きな脂肪を投げClassCastException
ます。
したがって、ダウンキャストしている場合は、instanceof
テストを使用して失敗を回避する必要があります。
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
今、疑問が浮かびます。なぜ地獄コンパイラは、最終的にそれがスローされるときにダウンキャストを許可しているのかjava.lang.ClassCastException
ですか?
答えは、コンパイラができることは、2つの型が同じ継承ツリーにあることを確認することだけなので、ダウンキャストの前にコードが何であるかに応じて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
上記の両方のアップキャストは例外なく正常に機能します。なぜなら、犬はIS-A動物であり、動物ができること、犬ができることを信じているからです。しかし、それは真の逆ではありません。