Elaborazione della risposta data da Michael Berry.
Dog d = (Dog)Animal; //Compiles but fails at runtime
Qui stai dicendo al compilatore "Fidati di me. So che d
si riferisce davvero a un Dog
oggetto" anche se non lo è.
Ricorda che il compilatore è costretto a fidarsi di noi quando facciamo un downcast .
Il compilatore conosce solo il tipo di riferimento dichiarato. La JVM in fase di esecuzione sa cosa sia realmente l'oggetto.
Quindi quando la JVM in fase di esecuzione capisce che si Dog d
sta effettivamente riferendo a un oggetto Animal
e non a un Dog
oggetto, lo dice. Ehi ... hai mentito al compilatore e hai lanciato un grosso grassoClassCastException
.
Quindi, se stai effettuando il downcasting, dovresti usare instanceof
test per evitare di sbagliare.
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
Ora ci viene in mente una domanda. Perché il compilatore infernale sta permettendo il downcast quando alla fine lancerà ajava.lang.ClassCastException
?
La risposta è che tutto ciò che il compilatore può fare è verificare che i due tipi si trovino nello stesso albero di ereditarietà, quindi a seconda di qualunque codice sia arrivato prima del downcast, è possibile che animal
sia di tipo dog
.
Il compilatore deve consentire cose che potrebbero funzionare in fase di esecuzione.
Considera il seguente snipet di codice:
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;
}
Tuttavia, se il compilatore è sicuro che il cast non funzionerà, la compilazione fallirà. IE Se si tenta di eseguire il cast di oggetti in gerarchie ereditarie diverse
String s = (String)d; // ERROR : cannot cast for Dog to String
A differenza del downcasting, l'upcasting funziona implicitamente perché quando esegui l'upgrade stai implicitamente limitando il numero di metodi che puoi invocare, al contrario del downcasting, il che implica che in seguito potresti voler invocare un metodo più specifico.
Dog d = new Dog();
Animal animal1 = d; // Works fine with no explicit cast
Animal animal2 = (Animal) d; // Works fine with n explicit cast
Entrambi gli upcast di cui sopra funzioneranno bene senza alcuna eccezione perché un cane IS-A Animal, anithing an Animal sa fare, un cane può fare. Ma non è vero viceversa.