Élaboration de la réponse donnée par Michael Berry.
Dog d = (Dog)Animal; //Compiles but fails at runtime
Ici, vous dites au compilateur "Faites-moi confiance. Je sais que cela d
fait vraiment référence à un Dog
objet" bien que ce ne soit pas le cas.
N'oubliez pas que le compilateur est obligé de nous faire confiance lorsque nous faisons un downcast .
Le compilateur ne connaît que le type de référence déclaré. La JVM au moment de l'exécution sait ce qu'est réellement l'objet.
Ainsi, lorsque la JVM au moment de l'exécution comprend que le Dog d
fait référence à Animal
un Dog
objet et non à un objet, il le dit. Hé ... tu as menti au compilateur et jette un gros morceau ClassCastException
.
Donc, si vous êtes abattu, vous devez utiliser le instanceof
test pour éviter de vous tromper.
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
Maintenant, une question nous vient à l'esprit. Pourquoi diable le compilateur autorise-t-il le downcast quand finalement il va lancer un java.lang.ClassCastException
?
La réponse est que tout ce que le compilateur peut faire est de vérifier que les deux types sont dans la même arborescence d'héritage, donc en fonction du code qui aurait pu venir avant le downcast, il est possible qu'il animal
soit de type dog
.
Le compilateur doit autoriser les choses qui pourraient fonctionner au moment de l'exécution.
Considérez l'extrait de code suivant:
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;
}
Cependant, si le compilateur est sûr que la distribution ne fonctionnerait pas, la compilation échouera. IE Si vous essayez de convertir des objets dans différentes hiérarchies d'héritage
String s = (String)d; // ERROR : cannot cast for Dog to String
Contrairement à la conversion descendante, la conversion ascendante fonctionne implicitement car lorsque vous effectuez une conversion ascendante, vous limitez implicitement le nombre de méthodes que vous pouvez invoquer, contrairement à la méthode descendante, ce qui implique que plus tard, vous souhaiterez peut-être invoquer une méthode plus spécifique.
Dog d = new Dog();
Animal animal1 = d; // Works fine with no explicit cast
Animal animal2 = (Animal) d; // Works fine with n explicit cast
Les deux upcast ci-dessus fonctionneront très bien sans aucune exception car un chien est un animal, tout comme un animal peut le faire, un chien peut le faire. Mais ce n'est pas vrai vica-versa.