Upcasting y Downcasting
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 30 de 87.
En la lección anterior, viste cómo una variable de tipo padre puede contener un objeto hijo: Animal myPet = new Dog(). Esta asignación se llama upcasting, y ocurre automáticamente porque un Dog siempre es un Animal.
Upcasting se mueve hacia arriba en la jerarquía de herencia. Es implícito y siempre seguro:
Dog dog = new Dog();
Animal animal = dog; // Upcasting - automáticoCuando realizas un upcast, solo puedes acceder a los métodos definidos en la clase padre. Los métodos específicos de la clase hija se ocultan, aunque el objeto siga siendo un Dog internamente.
Downcasting va en la dirección opuesta, convirtiendo una referencia de padre de nuevo a un tipo hijo. Esto requiere una conversión explícita (cast) porque no siempre es seguro:
Animal animal = new Dog(); // Upcasting
Dog dog = (Dog) animal; // Downcasting - se requiere conversión explícita
dog.bark(); // Ahora podemos acceder a los métodos de DogEl downcasting es arriesgado. Si el objeto no es realmente del tipo al que se está realizando el casting, Java lanza una ClassCastException en tiempo de ejecución:
Animal animal = new Cat();
Dog dog = (Dog) animal; // ¡Compila, pero falla en tiempo de ejecución!El compilador no puede detectar este error porque solo ve los tipos declarados. El tipo de objeto real solo se conoce cuando el programa se ejecuta. En la próxima lección, aprenderás cómo verificar de forma segura el tipo de un objeto antes de realizar un downcasting.
Desafío
FácilConstruyamos un sistema de reproductor de medios que demuestre tanto el upcasting como el downcasting en acción. Crearás una jerarquía de tipos de medios y practicarás la conversión entre referencias de padre e hijo.
Organizarás tu código en cuatro archivos:
Media.java: Crea la clase base que comparten todos los tipos de medios. Cada elemento de medio tiene un campotitle(String). Incluye un constructor para inicializarlo, un método gettergetTitle()y un métodoplay()que imprima:"Playing: " + titleSong.java: Crea una clase que extienda de Media. Las canciones tienen un campo adicionalartist(String). Usasuperpara la inicialización del padre. Sobrescribeplay()para imprimir:"Playing song: " + title + " by " + artist. Añade un métodoshowArtist()que imprima:"Artist: " + artistPodcast.java: Crea otra clase que extienda de Media. Los podcasts tienen un campoepisode(int). Sobrescribeplay()para imprimir:"Playing podcast: " + title + " - Episode " + episode. Añade un métodoshowEpisode()que imprima:"Episode: " + episodeMain.java: Demuestra el upcasting y downcasting con tus tipos de medios. Recibirás cuatro entradas: un título de canción, un nombre de artista, un título de podcast y un número de episodio.Primero, crea un Song y realiza un upcast a una referencia de Media. Llama a
play()en esta referencia para ver el polimorfismo en tiempo de ejecución en acción.Luego, realiza un downcast de esa referencia de Media de vuelta a un Song y llama a
showArtist()para acceder al método específico del hijo.Después, crea un Podcast y realiza un upcast a una referencia de Media. Llama a
play()en esta referencia.Finalmente, realiza un downcast de esa referencia de Media de vuelta a un Podcast y llama a
showEpisode().
Recibirás cuatro entradas: el título de la canción (String), el nombre del artista (String), el título del podcast (String) y el número de episodio (int).
Tu salida debería mostrar cuatro líneas: dos para la canción (su salida de reproducción y la información del artista) y dos para el podcast (su salida de reproducción y la información del episodio). ¡Observa cómo el upcasting te permite tratar diferentes tipos de medios de manera uniforme, mientras que el downcasting te permite acceder a sus características únicas!
Hoja de referencia
En Java, el upcasting (conversión ascendente) sube en la jerarquía de herencia y ocurre automáticamente. Un objeto hijo puede ser asignado a una variable de tipo padre:
Dog dog = new Dog();
Animal animal = dog; // Upcasting - automáticoCuando realizas un upcasting, solo puedes acceder a los métodos definidos en la clase padre, aunque el objeto siga siendo del tipo hijo internamente.
Downcasting (conversión descendente) convierte una referencia de padre de vuelta a un tipo hijo. Requiere un cast explícito:
Animal animal = new Dog(); // Upcasting
Dog dog = (Dog) animal; // Downcasting - se requiere un cast explícito
dog.bark(); // Ahora podemos acceder a los métodos de DogEl downcasting es arriesgado. Si el objeto no es realmente del tipo al que estás realizando el cast, Java lanza una ClassCastException en tiempo de ejecución:
Animal animal = new Cat();
Dog dog = (Dog) animal; // ¡Compila, pero falla en tiempo de ejecución!El compilador no puede detectar este error porque solo ve los tipos declarados. El tipo de objeto real solo se conoce cuando el programa se ejecuta.
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leer entradas
String songTitle = scanner.nextLine();
String artistName = scanner.nextLine();
String podcastTitle = scanner.nextLine();
int episodeNumber = scanner.nextInt();
// TODO: Crear una Song (Canción) y realizar un upcast a una referencia de Media
// TODO: Llamar a play() en la referencia de Media (demuestra polimorfismo)
// TODO: Realizar un downcast de la referencia de Media de vuelta a Song
// TODO: Llamar a showArtist() en la referencia de Song
// TODO: Crear un Podcast y realizar un upcast a una referencia de Media
// TODO: Llamar a play() en la referencia de Media
// TODO: Realizar un downcast de la referencia de Media de vuelta a Podcast
// TODO: Llamar a showEpisode() en la referencia de Podcast
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator