Upcasting e dowcasting

28 respostas
Kassiane_Pretti

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…

28 Respostas

TangZero

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:

ViniGodoy

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:

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 }
}
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.

Kassiane_Pretti

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é?

B

É 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.

TangZero

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!!!!
ViniGodoy

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!”

Kassiane_Pretti

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…

ViniGodoy

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;

B

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…

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.

Kassiane_Pretti

ViniGodoy:

Agora, isso sim, seria possível:

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

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?

Kassiane_Pretti

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?

ViniGodoy

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.

victorwss

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.

Kassiane_Pretti

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 =)

ViniGodoy

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”.

B

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?

F

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?

ViniGodoy

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.


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.

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

F

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  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

T

Você está confundindo “variáveis” com “objetos”.

Isso é um erro muito grave. É que em C++ existem variáveis que são objetos e variáveis que são referências para objetos; mas em Java todas as variáveis só contém referências para objetos.

Se uma variável é do tipo “Pai”, isso quer dizer que ela pode conter uma referência a um objeto da classe “Pai”, ou então da classe “Filho”. OK?

F

Não nao pode fica susse, isso nao to confundindo, soh ali na hora de dar os nomes q boiei…

Mas entendi oq vc kiz dizer no final…vlwww…

mas e dai o filho tem acesso aos metodos dele e aos do neto, mas também herdou os metodos do pai.

Entao agora pergunto eu, qual a diferença do filho e do pai, se eles tem conhecimento dos mesmos metodos, levando em conta que o pai enxerga os metodos do filho.

flw e vlw por enquanto

B

fr4nc0w:
mas e dai o filho tem acesso aos metodos dele e aos do neto, mas também herdou os metodos do pai.

Entao agora pergunto eu, qual a diferença do filho e do pai, se eles tem conhecimento dos mesmos metodos, levando em conta que o pai enxerga os metodos do filho.

Objetos podem acessar métodos deles próprios, e aqueles que foram herdados de outros acima na cadeira de herança.

Um objeto mais genérico não pode acessar métodos de objetos mais especializados, ou seja, abaixo na cadeia de herança.

F

Então qual a diferença entre.

Pai obj1 = new Filho(); // Eu só criei um objeto do tipo Pai correto? mas o objeto ainda é filho.

e

Filho obj2 = new Filho(); // Objeto é do tipo filho.

Mas entre criar um objeto do tipo pai ou filho, isso implicará em quais diferenças?

Sei que não posso:

Filho obj1 = new Pai(); // Criar um objeto PAI com o tipo Filho. pois ele não pode usar Metodos do filho e mtu menos ele sabe q filho existe, é isso?

B
fr4nc0w:
Então qual a diferença entre.

Pai obj1 = new Filho(); // Eu só criei um objeto do tipo Pai correto? mas o objeto ainda é filho.

e

Filho obj2 = new Filho(); // Objeto é do tipo filho.

Mas entre criar um objeto do tipo pai ou filho, isso implicará em quais diferenças?

A diferença é que você não vai poder acessar as interfaces de métodos que só existem na classe Filho.

Mamífero m = new Cachorro(); // todo cachorro é um mamífero

Cachorro c = new Cachorro(); // todo cachorro é um cachorro

Cachorro c = new Mamífero(); // Nem todo mamífero é um cachorro
F

Bruno Laturner:
fr4nc0w:
Então qual a diferença entre.

Pai obj1 = new Filho(); // Eu só criei um objeto do tipo Pai correto? mas o objeto ainda é filho.

e

Filho obj2 = new Filho(); // Objeto é do tipo filho.

Mas entre criar um objeto do tipo pai ou filho, isso implicará em quais diferenças?

A diferença é que você não vai poder acessar as interfaces de métodos que só existem na classe Filho.

Se eu criar um objeto filho do tipo pai neh?

B
fr4nc0w:
Bruno Laturner:
fr4nc0w:
Então qual a diferença entre.

Pai obj1 = new Filho(); // Eu só criei um objeto do tipo Pai correto? mas o objeto ainda é filho.

e

Filho obj2 = new Filho(); // Objeto é do tipo filho.

Mas entre criar um objeto do tipo pai ou filho, isso implicará em quais diferenças?

A diferença é que você não vai poder acessar as interfaces de métodos que só existem na classe Filho.

Se eu criar um objeto filho do tipo pai neh?

Filho do tipo Pai é muito confuso, vou usar exemplo do Mamífero e Cachorro:

Se você criar um objeto Cachorro e atribuí-lo à uma variável do tipo Mamífero, o mamífero não pode fazer coisas específicas de um cachorro.

Caso queira que um mamífero se comporte como um cachorro, há somente duas opções:

1) Utilizar da coerção para Mamífero se tornar um Cachorro.
Mamifero m = new Cachorro();
Cachorro c = (Cachorro) m; // Você, como programador, está garantindo que este mamífero é um cachorro.
2) Cachorro sobrescrever comportamentos/métodos definidos em Mamífero
public class Mamífero {

  public void falar() {
    System.out.println("...");
  }
}
public class Cachorro extends Mamífero {

  public void falar() {
    System.out.println("Au au!");
  }
}
Mamifero m = new Cachorro();
m.falar(); // imprime Au Au!

Note que falar não é um método específico de Cachorro, portanto não quebramos as regras.

F

E onde fica o Downcast e o Upcasting nos exemplos que voce me deu ali encima…

F

E onde fica o Downcast e o Upcasting nos exemplos que voce me deu ali encima…

Criado 23 de junho de 2008
Ultima resposta 7 de set. de 2008
Respostas 28
Participantes 7