Upcasting e dowcasting

Olá pessoal

Gostaria que alguém me explicasse esses dois conceitos de forma bem simplificada e de facil compreensão, pois já perguntei milhares de vezes para meu professor, mas não consegui enteder direito para q e como uso.
O que eu sei +/- é q o upcasting é feito de maneira automatica, essa conversão seria do filho para o pai, já o dowcasting é feita de maneira explicita e é a conversão do pai para o filho.
Mas se o pai não tem conhecimento de todos os métodos e atributos de seu filho, sendo este maior que seu pai, como seria automática a conversão? Olhando pelo outro lado, como todo pai está contido no seu filho, todo filho sabe a implementação do seu pai, mas a conversão deve ser explicitada, pq???

Tô meio confuça com estes conceitossss… :frowning:

Desde já agradeço a atenção e paciência de todos…

Considere esse relacionamento:

Avô <-- Pai <-- Filho

O upcasting, é quando um objeto de uma determinada classe, é referenciado por suas superclasses ou interfaces. Ou seja, você pode associar um objeto à uma referencia da superclasse ou interface desse objeto;

Avo pessoa1 = new Pai();
Pai pessoa2 = new Filho();
Filho pessoa3 = new Filho();
pessoa1 = pessoa3;

Nesse caso, o casting é implicito.

O dowcasting é quando você faz o processo contrário. Nese caso, o casting é explícito.

Avo pessoa1 = new Filho();
Pai pessoa2 = (Pai) pessoa1;
Filho pessoa3 = (Filho) pessoa1;

Sei que está meio confuso, mas não sei como explicar melhor. :cry:

A conversão do filho para o pai é sempre possível.

Isso porque, o filho, obrigatóriamente, tem todos os métodos do pai. O que acontece, é que vc acaba com uma variável que usa o filho de maneira simplificada. Mas correta.

Veja o exemplo:

[code]public abstract class Animal {
public abstract String barulho();
}

public class Cachorro extends Animal {
public String barulho() { return “auau!”; }
public void farejar() { //Fareja algo }
}

public class Cachorro extends Gato {
public String barulho() { return “miau!”; }
public void lamberOProprioCorpo() { //Toma banho }
}[/code]

Uma possível função que imprimisse o barulho de um animal qualquer na tela seria:

public void imprimeBarulho(Animal animal) { System.out.println(animal.barulho()); }

Veja, para a função, não interessa que os animais mais específicos tenham outros métodos. O que interessa é que todo animal, seja um cachorro, gato, papagaio ou galinha faz barulho. Como todo animal respeita a interface da classe pai, e implementa os métodos do pai, podemos, com certeza, dizer que enxergar um Gato ou Cachorro como um animal “qualquer” é possível.

Lembrando da relação de herança: Todo Cachorro é um Animal. Todo gato é um Animal.

Por outro lado, o oposto não é correto. Se temos em mãos um animal “qualquer”:
Animal animal = leAnimal();

Podemos fazer:
Gato gato = animal;
?

Não. Aquele animal poderia ser um cachorro. Embora todo gato seja um animal, nem todo animal é um gato.

E então, estaríamos fazendo uma conversão errada de tipos. Aí está a insegurança da operação.

O que acontece é que as vezes você tem certeza de que o animal em questão é mesmo um gato. Então, o Java fornece uma maneira do programador dizer isso e forçar a conversão. Essa maneira é o downcast, usando o operador explícito:

if (animal instanceof Gato) { Gato gato = (Gato) animal; //Estou dizendo para o Java. Ei, tenho certeza, faça a conversão. }

E se não for um gato, e ainda sim vc usar o cast? Vai receber uma ClassCastException.

Espero não ter te confundido mais.

1 curtida

ViniGodoy, muito obrigada… consegui compreender melhor…

Mas vamos lá, quando eu converto um filho para o pai terei acesso a todos os métodos implemetados no filho ainda ou acessarei o q é de comum aos dois?

Poi quando faço um dowcasting do pai para o filho só terei acesso aos métodos implementados no pai q são comuns ao filho né?

É meio complicado explicar herança em temos de filho e pai para algumas coisas.

Veja, quando uma classe que estende de uma outra, dizemos que esta é uma especialização.

Por exemplo, Homem e Mulher são especializações de SerHumano.

Caso temos algo como SerHumano pessoa = new Homem();, não há uma conversão, pois Homem é um SerHumano. Se ele é um ser humano ele sabe fazer tudo que um ser humano faz, certo?

Agora façamos o contrário:

Mulher marjory = buscarSerHumano();

buscarSerHumano retorna um SerHumano, porém concretamente, SerHumano só pode ser Homem ou Mulher.

Diga-me: Você pode ser certeza que o método retorna uma Mulher? Se não, o compilador muito menos.

Agora digamos que você tenha certeza. O método retorna SerHumano, e não dá p/ mudá-lo. Você tem que forçar a coerção para Mulher. Forçando isso você fala p/ o compilador “eu tenho certeza que marjory é uma Mulher, eu tenho ciência dos meus atos, então deixe-me em paz”.

Em todo caso, se estiver errado, o código vai lançar um RonaldinhoException, erm, digo… ClassCastException.

1 curtida

Já sei…

Vamos tentar algo bem simples:

Imagine uma objeto que possua botôes coloridos e que seja utilizado para um fim qualquer:

SbrubblesBase
1 botão verde

SbrubblesTech
1 botão vermelho

SbrubblesBase <-- SbrubblesTech

Upcasting:

O casting é automático porque a JVM sabe que como SbrubblesTeche extende SbrubblesBase, SbrubblesTech possui toda a funcionalidade de um SbrubblesBase. Ou seja, você pode usa um SbrubblesTech aonde um SbrubblesBase é esperado.
Ex: Preciso de um Sbrubbles para poder apertar o botão verde! Legal, o SbrubblesBase tem um botão verde, mas SbrubblesTech também possui um botão verde devido a herança, então eu posso usar qualquer uma das 2 classes. A JVM sabe que uma classes filha, pode ser usada no lugar do pai, pois ela possui as mesmas características do pai, e algo mais.

SbrubblesBase sbrubble = new SbrubblesTech();

sbrubble é uma referência a um SbrubblesTech, mas "mascarada" como SbrubblesBase.

Downcasting:

O casting deve ser forçado, porque a JVM não pode ter certeza que aquela referência aponta para um objeto filho.
Ex: Preciso de um Sbrubbles para poder apertar o botão verde e o vermelho| Humm, sei que um objeto referenciado por SbrubblesTech possui os 2 botôes, mas será que um objeto referenciado por SbrubblesBase possui os 2 botôes?

SbrubblesBase sbrubble1 = new SbrubblesTech();
SbrubblesTech sbrubble2 = sbrubble1; // Isso não funciona. Não compila!!!!
SbrubblesTech sbrubble3 = (SbrubblesTech) sbrubble1; // Isso funciona.

sbrubble1 é uma referência a um SbrubblesTech, mas "mascarada" como SbrubblesBase. O casting explícito informa a JVM que aquele objeto está "disfarçado".

Mas cuidado, se você tentar "desmascarar" um objeto inválido, você receberá uma excessão!

SbrubblesBase sbrubble1 = new SbrubblesBase();
SbrubblesTech sbrubble2 = (SbrubblesTech) sbrubble1; // Isso não funciona. Vai lançar excessão!!!!
1 curtida

Só o que é comum nos dois.

Não, a todos os métodos do filho. O que acontece na verdade é que o casting só muda o modo como o Java interpreta o objeto, não o objeto em si.

Quando vc faz:

Gato gato = (Gato)animal; gato.lamberOProprioCorpo(); //ok

Você está falando “Ei Java, aquele animal ali é um gato.”
A variável animal tem que ter um gato dentro dela. Ou seja, em algum momento um:

Animal animal = gato; 

Tem que ter sido dado.

Você pode imaginar isso assim. Imagine que você aponte o dedo para um gatinho e pergunte para uma criança:
“Que animal é aquele?” (Animal animal = gato; )

A criança vai responder: “É um gato.”

Se você apontar para um cachorrinho em seguida, e perguntar a mesma coisa:
“Que animal é aquele?”
A criança continua respondendo: “É um cachorro”.

Note que, embora vc esteja falando de “Animais” o objeto “Gato” ou “Cachorro” continua sendo um gato, ou um cachorro. No Java, isso também é verdade.

Animal animal = gato; animal instanceof Gato // (é true, o animal é mesmo um gato).

Agora, se vc apontar para um cachorro, e perguntar para a criança, “Aquele gato se lambe?”
A criança vai dizer: “Ei! Esse animal não é um gato.” É exatamente essa situação aqui:

Animal animal = new Cachorro(); Gato gato = (Gato) animal;

Isso resulta num ClassCastException, que é a forma de dizer: “Ei! Aquele animal não é um gato!”

Hummmm…

Resuminho

Filho -> pai (acesso aos métodos e atributos comuns a ambos)

pai -> filho (pai terá acesso aos métodos do filho)

A questão é que no dowcasting muda o jeito de visualizar o objeto, mas como funciona na memória? Pois quando foi realizado o new em obj foi como pai, ai quando se dá o casting para filho serão acrescentados novos atributos e métodos…

Só será possível fazer downcasting se o objeto atribuído ao pai for um objeto do filho.

Se vc deu um new na classe pai, não conseguirá fazer cast para a filha. Se isso é possível no seu código:
Animal animal = new Animal();
Não será possível fazer:

Gato gato = (Gato) animal; //Erro! "O animal não é um gato!"

Agora, isso sim, seria possível:

Animal animal = new Gato(); Gato gato = (Gato) animal;

[quote=KassiPretti]Hummmm…

Resuminho

Filho -> pai (acesso aos métodos e atributos comuns a ambos)

pai -> filho (pai terá acesso aos métodos do filho)

A questão é que no dowcasting muda o jeito de visualizar o objeto, mas como funciona na memória? Pois quando foi realizado o new em obj foi como pai, ai quando se dá o casting para filho serão acrescentados novos atributos e métodos…[/quote]

Nada é adicionado e nada é destruído. Os dados permanecem sempre os mesmos, o que muda é a forma de ver o objeto. Se você coegir um Gato para um Animal, a única coisa que vai acontecer é que ele não vai mais poder acessar o métodos específicos de Gato.

[quote=ViniGodoy]
Agora, isso sim, seria possível:

Animal animal = new Gato(); Gato gato = (Gato) animal;[/quote]

Hummm

Polimorfismo…
Para amimal foi alocado um espaço para Gato, mas ele foi instaciado da classe pai.
Mas como ele é de natureza um gato eu posso fazer um dowcasting para Gato. Pois posso garantir que o animal em questão é um Gato.

No outro caso

Animal animal = new Animal();

Não podemos fazer um dowcasting pq o animal em questão não é um Gato por natureza, isto é, não foi alocado um espaço para os métodos e atributos de Gato para animal. Na verdade animal não é um Gato.

Certo?
Será q eu entendi o conceito agora?

Só mais uma duvida…
Depois de fazer isso:

Animal animal = new Gato();

Não terei acesso aos métodos de Gato não né?
Só poderei acessar quando fizer a conversão?

Exatamente.

O upcasting é possível pq todo Gato é um animal. A única coisa que fazemos é usar um conjunto restrito de métodos do objeto (presentes em Animal).

O contrário nem sempre é possível pq o objeto referenciado pela variável do animal nem sempre é um gato. Outro animal pode ter sido criado e estar na memória.

Que tal assim:

class SerVivo {}
class Animal extends SerVivo {}
class Mamifero extends Animal {}
class Cachorro extends Mamifero {}
class Poodle extends Cachorro {}

class Gato extends Mamifero {}

class Main {
    public metodo() {
        // m é uma referência do tipo mamífero.
        Mamifero m = new Poodle();

        // Downcasting aqui. Assuma que m é um cachorro, pois ALGUNS mamíferos são cachorros.
        Cachorro c = (Mamifero) m;

        // Upcasting aqui. Considere que c é um ser vivo. TODOS os cachorros são seres vivos.
        SerVivo s = (SerVivo) c;

        // Downcasting inválido. Assuma que m é um gato, mas na verdade é um cachorro! ClassCastException!
        Gato g = (Gato) m;

        // Cast inválido (não é nem downcasting e nem upcasting).
        // NENHUM cachorro é um gato. Erro de compilação!
        Gato h = (Gato) new Cachorro();
    }
}

Enfim, se você fazer um cast de Animal para Cachorro, você estará fazendo um downcasting (cast para um tipo mais específico). É possível que o cast falhe lançando ClassCastException.
Já um cast de Cachorro para Animal é o upcasting, que nunca falha.

Muito bommmm…
Adorei todas as explcações meninos…

Na verdade para fazer um dowcasting devo usar polimorfismo…

Agora posso fazer minha provinha mais tranquila…

Vlw pela atenção e principalmente pela paciência q vcs tiveram cmg =)

Como isso aqui é sempre válido:

SerVivo s = (SerVivo) c;

O java te dispensa do operador de cast explícito:

SerVivo s = c;

Se fizer daquele jeito, ele dá um warning de “unecessary cast”.

Na verdade, polimorfismo é para evitar fazer downcasting. Ou mais exatamente, evitar saber quem é a classe concreta que implementa os métodos. Não conhecendo qual o tipo de objeto instanciado, você diminui o acoplamento entre as classes, o que melhora o teu modelo orientado a objetos.

Pense bem:

É mais simples você saber que você está lidando somente com um animal, ou ter que precisar saber como tratar especialmente cada espécie desse reino?

Prova da Rafaela chegando e eu encontro um topico tao oportuno…q otimo…hehehee

Vamos ver se entendi. Minha cabeça funcionou assim:

Só posso fazer referencia a algo que está acima na herança, nunca posso dizer que 2 itens do mesmo nível, digamos 2 filhos, são um do tipo do outro. Também não posso forçar dizer que uma coisa da classe mae é do tipo da filha.

No pai posso fazer referencia a tudo que tenho no filho e nele mesmo.
O filho só pode fazer referencia a ele mesmo ou algo abaixo dele.

Eu to boiando ou oq eu disse faz algum sentido?

Rafaela, da ET/UFPR?

Exatamente. Certo, você nunca poderá fazer a conversão de dois tipos na mesma altura da hierarquia:
Gato gato = (Gato)umCachorro; //Não faz sentido

Por outro lado, ambos objetos são do mesmo tipo (nesse caso, o tipo animal, ou o tipo ser vivo):

Animal animal = gato; //Ok, animal e gato são do mesmo tipo Animal outroAnimal = cachorro; //Ok, animal e cachorro são do mesmo tipo //Conclusão: Cachorro e gato são do tipo Animal.

[quote]
Só posso fazer referencia a algo que está acima na herança
No pai posso fazer referencia a tudo que tenho no filho e nele mesmo.
O filho só pode fazer referencia a ele mesmo ou algo abaixo dele.[/quote]

O que você quer dizer com “só posso fazer referência”?

Sim Vinicios, é que vi o nick ViniGodoy, ai já manjei que era você, aqui é o Robson da ET ex aluno de SO seu.

Então entenda fazer referencia como um modo leigo de dizer que posso usar metodos de um ou de outro.

Entao vamos a um exemplo do que eu quero entender e vamos ver se entendi.
é por que todos exemplos que vejo sobre isso ainda parecem mtu subjetivos para mim.

Pai
Filho
Neto

Pai var = new Filho();  // Crio um objeto do tipo Pai. é isso que fiz? hehehe

Filho var2 = new Filho(); // Crio um objeto do tipo Filho, é isso que fiz e qual a diferença desse em relacao ao de cima?

Pai var3 = new Neto(); // Isso pode ser feito?

Filho var4 = new Neto(); // Também pode fazer eu acho.

Neto var5 = new Filho(); // Não posso pois o filho está acima do neto e é assim q entendo pq nao posso fazer se existe outro motivo me ferrei pq to perdido entao.

Neto var6 = new Neto(); // Posso né e qual a diferença novamente em relacao a criar direto ele ou Pai var = new net();

DO que fiz ai encima, minhas idéias estão corretas? onde está o Downcasting e o Upcasting ali.

Qndo faço PAI VAR = NEW NETO();

Oq estou dizendo? ESTOU CRIANDO UM OBJETO NETO COM O TIPO PAI?

e NETO VAR = NEW NETO(); Um objeto neto do tipo neto?

Orientado a Objeto ainda é um mistério para mim pelo visto.

Vlw mesmo por enquanto.

flw