Elaborando a resposta dada por Michael Berry.
Dog d = (Dog)Animal; //Compiles but fails at runtime
Aqui você está dizendo ao compilador "Confie em mim. Eu sei que d
realmente está se referindo a um Dog
objeto", embora não seja.
Lembre-se de que o compilador é forçado a confiar em nós quando fazemos um downcast .
O compilador conhece apenas o tipo de referência declarado. A JVM em tempo de execução sabe qual é realmente o objeto.
Portanto, quando a JVM no tempo de execução descobre que na Dog d
verdade se refere a Animal
um Dog
objeto e não a um objeto, diz. Ei ... você mentiu para o compilador e jogou muita gordura ClassCastException
.
Portanto, se você está instanceof
fazendo downcast, deve usar o teste para evitar estragar.
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
Agora uma pergunta vem à nossa mente. Por que o compilador infernal está permitindo o downcast quando, eventualmente, ele vai lançar umjava.lang.ClassCastException
?
A resposta é que tudo o que o compilador pode fazer é verificar se os dois tipos estão na mesma árvore de herança, portanto, dependendo do código que possa ter vindo antes do downcast, é possível que animal
seja do tipo dog
.
O compilador deve permitir coisas que possam funcionar em tempo de execução.
Considere o seguinte trecho de código:
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;
}
No entanto, se o compilador tiver certeza de que o elenco não funcionaria, a compilação falhará. IE Se você tentar converter objetos em diferentes hierarquias de herança
String s = (String)d; // ERROR : cannot cast for Dog to String
Diferentemente da downcasting, a upcasting funciona implicitamente porque, quando você upcast, está implicitamente restringindo o número de métodos que você pode invocar, ao contrário do downcasting, o que implica que, posteriormente, você poderá invocar um método mais específico.
Dog d = new Dog();
Animal animal1 = d; // Works fine with no explicit cast
Animal animal2 = (Animal) d; // Works fine with n explicit cast
Ambos os upcast acima funcionarão bem, sem exceção, porque um cão é um animal, anithing um animal pode fazer, um cão pode fazer. Mas não é verdade vica-versa.