vazamento explícito de superclass para subclass

public class Animal { public void eat() {} } public class Dog extends Animal { public void eat() {} public void main(String[] args) { Animal animal = new Animal(); Dog dog = (Dog) animal; } } 

A designação Dog dog = (Dog) animal; não gera um erro de compilation, mas no tempo de execução gera um ClassCastException . Por que o compilador não consegue detectar esse erro?

Ao usar um casting, você está essencialmente dizendo ao compilador: “Confie em mim. Sou um profissional, sei o que estou fazendo e sei que, embora não possa garantir isso, estou lhe dizendo que essa variável animal é definitivamente vai ser um cachorro “.

Já que o animal não é realmente um cachorro (é um animal, você poderia fazer Animal animal = new Dog(); e seria um cachorro) a VM lança uma exceção em tempo de execução porque você violou essa confiança (você disse o compilador tudo estaria ok e não é!

O compilador é um pouco mais esperto do que aceitar cegamente tudo, se você tentar e converter objects em hierarquias de inheritance diferentes (converter um Dog em uma String, por exemplo), o compilador o jogará de volta em você, porque sabe que isso nunca funcionaria.

Como você está basicamente impedindo o compilador de reclamar, toda vez que você converter é importante verificar se você não causará um ClassCastException usando instanceof em uma instrução if (ou algo assim).

Porque teoricamente Animal animal pode ser um cachorro:

 Animal animal = new Dog(); 

Geralmente, downcasting não é uma boa ideia. Você deve evitar isso. Se você usá-lo, é melhor include um cheque:

 if (animal instanceof Dog) { Dog dog = (Dog) animal; } 

Para evitar esse tipo de ClassCastException, se você tiver:

 class A class B extends A 

Você pode definir um construtor em B que leva um object de A. Desta forma, podemos fazer o “cast”, por exemplo:

 public B(A a) { super(a.arg1, a.arg2); //arg1 and arg2 must be, at least, protected in class A // If B class has more attributes, then you would initilize them here } 

Dog d = (Dog)Animal; //Compiles but fails at runtime

Aqui você está dizendo para o compilador “Confie em mim. Eu sei d está realmente se referindo a um object Dog “, embora não seja. Lembre-se de que o compilador é forçado a confiar em nós quando fazemos um downcast .

O compilador só sabe sobre o tipo de referência declarado. A JVM em tempo de execução sabe o que o object realmente é.

Então, quando a JVM no tempo de execução descobre que o Dog d está realmente se referindo a um object Animal e não a um Dog ele diz. Ei … você mentiu para o compilador e jogou uma grande ClassCastException .

Então, se você está fazendo downcast, você deve usar o instanceof test para evitar estragar tudo.

if (animal instanceof Dog) { Dog dog = (Dog) animal; }

Agora uma pergunta vem à nossa mente. Por que o compilador do inferno está permitindo o downcast quando, eventualmente, ele vai lançar um java.lang.ClassCastException ?

A resposta é que tudo que o compilador pode fazer é verificar se os dois tipos estão na mesma tree de inheritance, então, dependendo do código que possa vir antes do downcast, é possível que o animal seja do tipo dog .

O compilador deve permitir coisas que possam funcionar em tempo de execução.

Considere o seguinte snipet 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 a conversão não funcionará, a compilation falhará. IE Se você tentar converter objects em diferentes hierarquias de inheritance

String s = (String)d; // ERROR : cannot cast for Dog to String

Ao contrário do downcasting, o upcasting funciona implicitamente porque, quando você faz o upcast, você está implicitamente restringindo o número de methods que pode invocar, como oposto ao downcasting, o que implica que mais tarde, você pode querer 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 qualquer exceção, porque um cão é um animal, um animal pode fazer, um cão pode fazer. Mas não é verdade vica-versa.

O código gera um erro de compilation porque seu tipo de instância é um Animal:

 Animal animal=new Animal(); 

Downcasting não é permitido em Java por vários motivos. Veja aqui para detalhes.

Como explicado, isso não é possível. Se você quiser usar um método da subclass, avalie a possibilidade de adicionar o método à superclass (pode estar vazio) e chame a partir das subclasss obtendo o comportamento desejado (subclass) graças ao polymorphism. Então, quando você chamar d.method (), a chamada terá sucesso sem a conversão, mas caso o object não seja um cachorro, não haverá problema