Elaborando la respuesta dada por Michael Berry.
Dog d = (Dog)Animal; //Compiles but fails at runtime
Aquí le estás diciendo al compilador "Confía en mí. Sé d
que realmente se refiere a un Dog
objeto", aunque no lo es.
Recuerde que el compilador se ve obligado a confiar en nosotros cuando hacemos un downcast .
El compilador solo conoce el tipo de referencia declarado. La JVM en tiempo de ejecución sabe cuál es realmente el objeto.
Entonces, cuando la JVM en el tiempo de ejecución descubre que en Dog d
realidad se refiere a un objeto Animal
y no a un Dog
objeto, dice. Oye ... mentiste al compilador y arrojas una gran grasa ClassCastException
.
Entonces, si está abatido, debe usar la instanceof
prueba para evitar arruinarlo.
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
Ahora una pregunta viene a nuestra mente. ¿Por qué el compilador del infierno está permitiendo el downcast cuando eventualmente lanzará un java.lang.ClassCastException
?
La respuesta es que todo lo que el compilador puede hacer es verificar que los dos tipos estén en el mismo árbol de herencia, por lo que, dependiendo del código que haya llegado antes del downcast, es posible que animal
sea de tipo dog
.
El compilador debe permitir cosas que posiblemente funcionen en tiempo de ejecución.
Considere el siguiente fragmento 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;
}
Sin embargo, si el compilador está seguro de que el reparto no funcionaría, la compilación fallará. IE Si intenta convertir objetos en diferentes jerarquías de herencia
String s = (String)d; // ERROR : cannot cast for Dog to String
A diferencia del downcasting, el upcasting funciona implícitamente porque cuando realiza un upcast está restringiendo implícitamente el número de métodos que puede invocar, en contraposición al downcasting, lo que implica que más adelante, es posible que desee invocar un método más 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
Los dos cambios anteriores funcionarán bien sin ninguna excepción porque un perro es un animal IS-A, lo que un animal puede hacer, un perro puede hacer. Pero no es cierto, viceversa.