Blogger templates

Comunidade java aberto - Participe da nossa comunidade no facebook (clique aqui)

Visitas

quarta-feira, 10 de outubro de 2012

(Java Jr - Parte XII) - Conversão de tipos de objetos (Casting)

,
Não sei se alguem já notou mas as anotações sobre Java que estou postando aqui são apropriadas para quem vai prestar o exame SCJP (Sun Certified Java Programmer).

Como também pretendo ser um programador certificado pela Sun, estou estudando e quero aproveitar para compartilhar com você meus resumos.

Hoje vou falar sobre o objetivo 5.2 do exame que é sobre Conversão de Variáveis de Referência (também conhecido por Cast ou Casting de tipos):

Dado um cenário, desenvolver código que demonstre o uso do polimorfismo. Além disso, determinar quando a conversão será necessária e saber diferenciar erros de compilação de erros de tempo de execução relacionados a conversão de referências a objetos.

Temos 2 pontos principais neste objetivo:
Uso do polimorfismo e saber diferenciar erros de compilação e de tempo de execução. 

Veja também que no final está"referências a objetos"

Isto quer dizer que esse tipo de conversão não é como conversão entre tipos primitivos (que não será abordado neste post).

Vamos começar analisando o código:
  1. //Superclasse  
  2. public class Animal {  
  3.    
  4.  void comer(){  
  5.   System.out.println("Animal comendo...");  
  6.  }  
  7.    
  8. }  
  9. //Subclasse  
  10. class Dog extends Animal{  
  11.    
  12.  void rolar(){  
  13.   System.out.println("Dog rolando...");  
  14.  }  
  15. }  

Upcasting ou Conversão ampliadora

Considerando o código acima poderíamos naturalmente fazer as seguintes declarações:
  1. Animal animal; //Declarou um tipo genérico  
  2. animal = new Dog(); //Apontou para um mais específico  
  3. //Ou simplesmente:  
  4. Animal animal = new Dog();  
  5. //Ou ainda:  
  6. Animal animal = (Animal)new Dog(); //Conversão explícita  

Porque? Tipos genéricos podem apontar para tipos mais específicos. Isto chama-seupcasting ou conversão ampliadora. Esta conversão é sempre segura, pois estamos indo de um tipo mais específico para um mais genérico (de baixo pra cima na árvore de herança) e sabemos que a superclasse poderá fazer tudo que a subclasse fazia pois herdou dela. Mas fazendo isto estamos perdendo os métodos de Dog. 
Nesse caso a variável de referência animal que é do tipo Animal faz referência a Dog e isto é possível porque Dog É-UM Animal (Este relacionamento É-UM significa que Dog extends Animal).
Por estes motivos o compilador permite fazer o cast sem nenhuma notação especial nem nada explícito.


Até aqui tudo bem, mas e se tentar realizar um comportamento mais específico? Por exemplo, tentar chamar um método que só existe em Dog? Não pode porque só comportamentos de Animal serão aceitos:
  1. animal.rolar(); //Problema! Método não definido para o tipo Animal  

O código não compila porque animal não tem o método rolar(). Entenda que estamos transformando um Dog num tipo mais genérico e que ele agora só poderá executar ações deste novo tipo. Ele perdeu os comportamentos específicos que tinha. 
Mas um coisa interessante é que se Dog sobrescrever algum método de Animal, qual dos métodos será executado? Vamos modificar o código:
  1. //Superclasse  
  2. public class Animal {  
  3.    
  4.  void comer(){  
  5.   System.out.println("Animal comendo...");  
  6.  }  
  7.    
  8. }  
  9. //Subclasse  
  10. class Dog extends Animal{  
  11.    
  12.  void rolar(){  
  13.   System.out.println("Dog rolando...");  
  14.  }  
  15.  //Dog sobrescreveu o método comer  
  16.  void comer(){  
  17.   System.out.println("Dog comendo...");  
  18.  }  
  19. }  

Suponhamos que faça a seguinte chamada:
  1. Animal animal = new Dog();  
  2. animal.comer();  

Quem vai comer? Animal ou Dog? Neste caso o método em Dog seria executado porque ele sobrescreveu. Então quando fazemos o upcasting perdemos os métodos do subtipo mas se algum for sobrescrito a versão a ser executada é a do subtipo. Muito louco isso não é?

Downcasting 

Seguindo a raciocínio do upcasting, se quisermos "voltar" um tipo genérico para o mais específico (conversão redutora), devemos fazer a conversão explicitamente:
  1. Dog dog = (Dog) animal;  
  2. dog.comer();  

Veja que mudamos apenas o tipo da variável, não o tipo da referência. Então esta conversão se torna possível pois animal faz referência a Dog.
Então você se pergunta: Porque o upcasting é automático e o downcasting não? Agora chegamos num ponto interessante: No upcasting a conversão nunca falha. Nodowncasting, nem sempre teremos esta certeza. No exemplo citado acima, a conversão deu certo porque animal fazia referência a Dog mas e se não fizesse? Se animal fizesse referência a Animal mesmo?
  1. Animal animal = new Animal();  
  2. Dog dog = (Dog) animal;  

Como isso poderá ser feito se Animal não é um Dog? Animal nem sabe da existência de Dog. A conversão não pode ser feita porque os tipos são incompatíveis.
Só que o mais intrigante vem agora: O código compila! 
Se você usa o Eclipse vá em Project > Clean..., selecione seu projeto e veja se apareceu algum erro em Problems? Não vai aparecer nada, porque pro compilador está tudo bem, ele não sabe e nem quer saber se animal faz referência a Animal ou Dog. Mas quando tentar executar:
Exception in thread "main" java.lang.ClassCastException: Animal cannot be cast to Dog
at Animal.main(Animal.java:46)

Então não tente converter um objeto para um tipo que ele não é. Mas lembre-se que o compilador não sabe se você vai tentar isto.
Para saber se dada conversão é aceita devemos usar o operador instanceof. Este operador retorna true ou false para uma comparação entre tipos:
  1. if (dog instanceof Animal)  
  2. {  
  3.  System.out.println("dog é um Animal.");  
  4. }  

Na IDE Eclipse, quando começar a digitar "ins" e pressionar Ctrl + Barra de espaço ele já sugere o operador, então pressionando Enter ele já vai preencher um bloco de código igual a este:
  1. if (name instanceof type) {  
  2.  type new_name = (type) name;  
  3.    
  4. }  

Aí é só você ir dando TAB e preenchendo os campos. Ele faz isso porque sabe que quase sempre que usamos o instanceof é pra saber se um objeto é de determinado tipo, e sendo positivo, já queremos que o objeto seja convertido neste tipo.
Aproveitando que já dei essa dica, tem outras duas que eu também uso muito para criar o método main e pro comando System.out.println: Para o main apenas digite main e Ctrl + Barra de espaço, para o println digite sysout Ctrl + Barra de espaço.

Só por curiosidade: Podemos converter qualquer objeto no tipo Object, como já falei no outro post, o Object é como uma classe "mãe" no Java:
  1. Object obj = (Object)new Animal();  

Mas essa conversão não tem muita utilidade pois faz com que animal perca todos seus métodos como já foi explicado. Por hoje é só!

Bons estudos e boas conversões!

Fonte(Javacomcafe)

0 comentários to “(Java Jr - Parte XII) - Conversão de tipos de objetos (Casting)”

Postar um comentário

Insira seu comentário

 

Java Aberto Copyright © 2011 -- Template created by O Pregador -- Powered by Blogger