Upcasting e Downcasting
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 30 de 87.
Na lição anterior, você viu como uma variável do tipo pai pode conter um objeto filho: Animal myPet = new Dog(). Essa atribuição é chamada de upcasting, e ela acontece automaticamente porque um Dog é sempre um Animal.
Upcasting sobe na hierarquia de herança. É implícito e sempre seguro:
Dog dog = new Dog();
Animal animal = dog; // Upcasting - automáticoQuando você realiza o upcasting, você só pode acessar métodos definidos na classe pai. Os métodos específicos da classe filha tornam-se ocultos, embora o objeto ainda seja um Dog por baixo.
Downcasting vai na direção oposta, convertendo uma referência de pai de volta para um tipo filho. Isso requer um cast explícito porque nem sempre é seguro:
Animal animal = new Dog(); // Upcasting
Dog dog = (Dog) animal; // Downcasting - conversão explícita necessária
dog.bark(); // Agora podemos acessar os métodos de DogO downcasting é arriscado. Se o objeto não for realmente o tipo para o qual você está convertendo, o Java lança uma ClassCastException em tempo de execução:
Animal animal = new Cat();
Dog dog = (Dog) animal; // Compila, mas ocorre um erro em tempo de execução!O compilador não consegue capturar este erro porque ele apenas vê os tipos declarados. O tipo real do objeto só é conhecido quando o programa é executado. Na próxima lição, você aprenderá como verificar com segurança o tipo de um objeto antes de realizar o downcasting.
Desafio
FácilVamos construir um sistema de player de mídia que demonstra tanto o upcasting quanto o downcasting em ação. Você criará uma hierarquia de tipos de mídia e praticará a conversão entre referências de pai e filho.
Você organizará seu código em quatro arquivos:
Media.java: Crie a classe base que todos os tipos de mídia compartilham. Cada item de mídia possui um campotitle(String). Inclua um construtor para inicializá-lo, um método gettergetTitle()e um métodoplay()que imprime:"Playing: " + titleSong.java: Crie uma classe que estende Media. As músicas têm um campo adicionalartist(String). Usesuperpara a inicialização do pai. Sobrescrevaplay()para imprimir:"Playing song: " + title + " by " + artist. Adicione um métodoshowArtist()que imprime:"Artist: " + artistPodcast.java: Crie outra classe que estende Media. Os podcasts têm um campoepisode(int). Sobrescrevaplay()para imprimir:"Playing podcast: " + title + " - Episode " + episode. Adicione um métodoshowEpisode()que imprime:"Episode: " + episodeMain.java: Demonstre upcasting e downcasting com seus tipos de mídia. Você receberá quatro entradas: um título de música, um nome de artista, um título de podcast e um número de episódio.Primeiro, crie um Song e faça o upcast para uma referência Media. Chame
play()nesta referência para ver o polimorfismo em tempo de execução funcionando.Em seguida, faça o downcast dessa referência Media de volta para um Song e chame
showArtist()para acessar o método específico do filho.Depois, crie um Podcast e faça o upcast para uma referência Media. Chame
play()nesta referência.Finalmente, faça o downcast dessa referência Media de volta para um Podcast e chame
showEpisode().
Você receberá quatro entradas: o título da música (String), o nome do artista (String), o título do podcast (String) e o número do episódio (int).
Sua saída deve mostrar quatro linhas - duas para a música (sua saída de reprodução e informações do artista) e duas para o podcast (sua saída de reprodução e informações do episódio). Observe como o upcasting permite tratar diferentes tipos de mídia de forma uniforme, enquanto o downcasting permite acessar seus recursos exclusivos!
Folha de consulta
Em Java, o upcasting sobe na hierarquia de herança e acontece automaticamente. Um objeto filho pode ser atribuído a uma variável do tipo pai:
Dog dog = new Dog();
Animal animal = dog; // Upcasting - automáticoQuando você faz o upcasting, só pode acessar métodos definidos na classe pai, embora o objeto ainda seja do tipo filho por baixo.
O Downcasting converte uma referência de pai de volta para um tipo filho. Ele requer um cast explícito:
Animal animal = new Dog(); // Upcasting
Dog dog = (Dog) animal; // Downcasting - cast explícito necessário
dog.bark(); // Agora podemos acessar os métodos de DogO downcasting é arriscado. Se o objeto não for realmente do tipo para o qual você está convertendo, o Java lança uma ClassCastException em tempo de execução:
Animal animal = new Cat();
Dog dog = (Dog) animal; // Compila, mas trava em tempo de execução!O compilador não consegue capturar esse erro porque ele vê apenas os tipos declarados. O tipo real do objeto só é conhecido quando o programa é executado.
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler entradas
String songTitle = scanner.nextLine();
String artistName = scanner.nextLine();
String podcastTitle = scanner.nextLine();
int episodeNumber = scanner.nextInt();
// TODO: Criar uma Song e fazer o upcast para uma referência Media
// TODO: Chamar play() na referência Media (demonstra polimorfismo)
// TODO: Fazer o downcast da referência Media de volta para Song
// TODO: Chamar showArtist() na referência Song
// TODO: Criar um Podcast e fazer o upcast para uma referência Media
// TODO: Chamar play() na referência Media
// TODO: Fazer o downcast da referência Media de volta para Podcast
// TODO: Chamar showEpisode() na referência Podcast
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator