Oi! Em uma lição anterior, discutimos a conversão de tipos primitivos. Vamos relembrar brevemente o que foi discutido. Imaginamos tipos primitivos (neste caso, tipos numéricos) como bonecos aninhados que variam em tamanho de acordo com a quantidade de memória que ocupam. Como você deve se lembrar, colocar um boneco menor dentro de um maior é simples tanto na vida real quanto na programação Java.
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
short smallNumber = (short) bigNumber;
System.out.println(smallNumber);
}
}
Este é um exemplo de conversão ou ampliação automática . Isso acontece por si só, então você não precisa escrever código adicional. No final, não estamos fazendo nada incomum: estamos apenas colocando uma boneca menor em uma boneca maior. É outra questão se tentarmos fazer o oposto e colocar uma boneca russa maior em uma boneca menor. Você não pode fazer isso na vida real, mas na programação você pode. Mas há uma nuance. Se tentarmos colocar an int
em uma short
variável, as coisas não correrão tão bem para nós. Afinal, a short
variável contém apenas 16 bits de informação, mas an int
ocupa 32 bits! Como resultado, o valor passado é distorcido. O compilador nos dará um erro (' Cara, você está fazendo algo suspeito!'). Mas se indicarmos explicitamente o tipo para o qual estamos convertendo nosso valor, ele prosseguirá e executará a operação.
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
bigNumber = (short) bigNumber;
System.out.println(bigNumber);
}
}
Foi exatamente o que fizemos no exemplo acima. A operação foi executada, mas como a short
variável pode acomodar apenas 16 dos 32 bytes, o valor final é distorcido e obtemos o número -27008 . Essa operação é chamada de conversão explícita ou restrição .
Exemplos de ampliação e restrição de tipos de referência
Agora vamos falar sobre os mesmos operadores aplicados não a tipos primitivos, mas a objetos e variáveis de referência ! Como isso funciona em Java? Na verdade, é bem simples. Existem objetos que não estão relacionados. Seria lógico supor que eles não podem ser convertidos entre si, nem explícita nem automaticamente:
public class Cat {
}
public class Dog {
}
public class Main {
public static void main(String[] args) {
Cat cat = new Dog(); // Error!
}
}
Aqui, é claro, temos um erro. As classes Cat
e Dog
não estão relacionadas entre si e não escrevemos um 'conversor' para passar de uma para a outra. Faz sentido que não possamos fazer isso: o compilador não tem ideia de como converter esses objetos de um tipo para outro. Se os objetos estão relacionados, bem, isso é outro assunto! Relacionado como? Acima de tudo, por herança. Vamos tentar usar herança para criar um pequeno sistema de classes. Teremos uma classe comum para representar os animais:
public class Animal {
public void introduce() {
System.out.println("I'm Animal");
}
}
Todo mundo sabe que os animais podem ser domesticados (pets) ou selvagens:
public class WildAnimal extends Animal {
public void introduce() {
System.out.println("I'm WildAnimal");
}
}
public class Pet extends Animal {
public void introduce() {
System.out.println("I'm Pet");
}
}
Por exemplo, pegue os caninos — temos cachorros domésticos e coiotes:
public class Dog extends Pet {
public void introduce() {
System.out.println("I'm Dog");
}
}
public class Coyote extends WildAnimal {
public void introduce() {
System.out.println ("I'm Coyote");
}
}
Escolhemos especificamente as classes mais básicas para torná-las mais fáceis de entender. Nós realmente não precisamos de nenhum campo, e um método é suficiente. Vamos tentar executar este código:
public class Main {
public static void main(String[] args) {
Animal animal = new Pet();
animal.introduce();
}
}
O que você acha que será exibido no console? O introduce
método da Pet
classe ou a Animal
classe será invocado? Tente justificar sua resposta antes de continuar a leitura. E aqui está o resultado! Eu sou um animal de estimação Por que conseguimos isso? É tudo simples. Temos uma variável pai e um objeto descendente. Por escrito,
Animal animal = new Pet();
ampliamos uma Pet
referência e atribuímos a ela uma Animal
variável. Assim como os tipos primitivos, os tipos de referência são ampliados automaticamente em Java. Você não precisa escrever código adicional para que isso aconteça. Agora temos um objeto descendente atribuído a uma referência pai. Como resultado, vemos que a chamada do método é feita na classe descendente. Se você ainda não entender completamente por que esse código funciona, reescreva-o em linguagem simples:
Animal animal = new DomesticatedAnimal();
Não há nenhum problema com isso, certo? Imagine que esta é a vida real, e a referência é simplesmente uma etiqueta de papel com 'Animal' escrito nela. Se você pegar aquele pedaço de papel e prendê-lo na coleira de qualquer animal de estimação, tudo ficará correto. Afinal, qualquer animal de estimação é um animal! O processo inverso — descendo na árvore de herança para os descendentes — está se estreitando:
public class Main {
public static void main(String[] args) {
WildAnimal wildAnimal = new Coyote();
Coyote coyote = (Coyote) wildAnimal;
coyote.introduce();
}
}
Como você pode ver, aqui indicamos claramente a classe para a qual queremos converter nosso objeto. Anteriormente, tínhamos uma WildAnimal
variável e agora temos um Coyote
, que é inferior na árvore de herança. Faz sentido que, sem uma indicação explícita, o compilador não permita tal operação, mas se indicarmos o tipo entre parênteses, tudo funcionará. Considere outro exemplo mais interessante:
public class Main {
public static void main(String[] args) {
Pet pet = new Animal(); // Error!
}
}
O compilador gera um erro! Mas por que? Porque você está tentando atribuir um objeto pai a uma referência descendente. Em outras palavras, você está tentando fazer algo assim:
DomesticatedAnimal domesticatedAnimal = new Animal();
Bem, talvez tudo funcione se especificarmos explicitamente o tipo para o qual estamos tentando converter? Isso funcionou com números - vamos tentar! :)
public class Main {
public static void main(String[] args) {
Pet pet = (Pet) new Animal();
}
}
Exceção no thread "main" java.lang.ClassCastException: Animal não pode ser convertido em Pet Erro! O compilador não gritou conosco desta vez, mas acabamos com uma exceção. Já sabemos o motivo: estamos tentando atribuir um objeto pai a uma referência descendente. Mas por que exatamente você não pode fazer isso? Porque nem todos os animais são animais domesticados. Você criou um Animal
objeto e está tentando atribuí-lo a uma Pet
variável. Um coiote também é um Animal
, mas não é um Pet
. Em outras palavras, quando você escreve
Pet pet = (Pet) new Animal();
new Animal()
poderia representar qualquer animal, não necessariamente um animal de estimação! Naturalmente, sua Pet pet
variável é adequada apenas para armazenar animais de estimação (e seus descendentes) e não qualquer tipo de animal. É por isso que uma exceção Java especial, ClassCastException
, foi criada para casos em que ocorre um erro durante a conversão de classes. Vamos revisá-lo novamente para tornar as coisas mais claras. Uma referência pai pode apontar para instâncias de uma classe descendente:
public class Main {
public static void main(String[] args) {
Pet pet = new Pet();
Animal animal = pet;
Pet pet2 = (Pet) animal;
pet2.introduce();
}
}
Por exemplo, aqui não temos problemas. Temos um Pet
objeto referenciado por uma Pet
variável. Mais tarde, uma Animal
referência apontava para o mesmo objeto. Depois disso, convertemos animal
para um arquivo Pet
. A propósito, por que isso funcionou para nós? Da última vez tivemos uma exceção! Porque desta vez nosso objeto original é um Pet
!
Pet pet = new Pet();
Mas no último exemplo, era um Animal
objeto:
Pet pet = (Pet) new Animal();
Você não pode atribuir um objeto ancestral a uma variável descendente. Você pode fazer o contrário.
GO TO FULL VERSION