Semana 9 Polimorfismo en Java
El polimorfismo es una característica de la programación orientada a objetos que permite que un mismo nombre, como el de un método, tenga diferentes comportamientos según el contexto. La palabra "polimorfismo" significa "muchas formas", lo que refleja su capacidad para adaptarse a distintas situaciones.
Analogía Simple
Piensa en un interruptor de luz. Al presionarlo, enciende una lámpara, pero en otro contexto podría activar un ventilador. Aunque el acción es la misma ("presionar"), el resultado depende del dispositivo conectado. El polimorfismo funciona de manera similar: un mismo nombre produce resultados diferentes según cómo se use.
Propósito
- Flexibilidad: Permite usar un nombre común para varias tareas, haciendo el código más claro.
- Reutilización: Reduce la necesidad de escribir código repetitivo.
- Mantenimiento: Facilita actualizar o modificar el código sin afectar otras partes.
En Java, el polimorfismo se presenta en dos formas: 1. En tiempo de compilación: Sobrecarga de métodos. 2. En tiempo de ejecución: Sobreescritura de métodos.
A continuación, se explica cada tipo con ejemplos prácticos.
1. Polimorfismo en Tiempo de Compilación: Sobrecarga de Métodos
La sobrecarga de métodos consiste en definir varios métodos con el mismo nombre dentro de una misma clase, pero con diferentes parámetros (en cantidad o tipo). Java selecciona el método adecuado según los argumentos proporcionados, y esta decisión se toma al compilar el programa.
Ejemplo: Clase para Sumar Números
Se creará una clase Calculadora
que suma números de diferentes formas: dos enteros, tres enteros o dos decimales.
Código
public class Calculadora {
// Método 1: Suma dos números enteros
public int sumar(int a, int b) {
return a + b;
}
// Método 2: Suma tres números enteros
public int sumar(int a, int b, int c) {
return a + b + c;
}
// Método 3: Suma dos números decimales
public double sumar(double a, double b) {
return a + b;
}
// Método principal para probar
public static void main(String[] args) {
Calculadora calc = new Calculadora();
// Suma de dos enteros
System.out.println("Resultado de 5 + 3: " + calc.sumar(5, 3));
// Suma de tres enteros
System.out.println("Resultado de 5 + 3 + 2: " + calc.sumar(5, 3, 2));
// Suma de dos decimales
System.out.println("Resultado de 5.5 + 3.2: " + calc.sumar(5.5, 3.2));
}
}
Salida
Resultado de 5 + 3: 8
Resultado de 5 + 3 + 2: 10
Resultado de 5.5 + 3.2: 8.7
Explicación Paso a Paso
- Definición de la Clase:
-
La clase
Calculadora
tiene tres métodos llamadossumar
:- Uno para dos enteros (
int
). - Otro para tres enteros (
int
). - Otro para dos decimales (
double
).
- Uno para dos enteros (
-
Selección del Método:
- Al llamar
sumar(5, 3)
, Java identifica que se usan dos enteros y ejecutasumar(int, int)
. - Con
sumar(5, 3, 2)
, detecta tres enteros y usasumar(int, int, int)
. -
Para
sumar(5.5, 3.2)
, seleccionasumar(double, double)
por los decimales. -
Relación con el Polimorfismo:
- El nombre
sumar
es el mismo, pero cada versión realiza una tarea distinta según los parámetros. Esto simplifica el uso del código, ya que solo se necesita un nombre.
Representación Visual
Imagina los métodos como versiones de una misma función:
sumar(int, int) → Para 2 enteros
sumar(int, int, int) → Para 3 enteros
sumar(double, double) → Para 2 decimales
2. Polimorfismo en Tiempo de Ejecución: Sobreescritura de Métodos
La sobreescritura de métodos ocurre cuando una clase hija redefine un método heredado de su clase padre, cambiando su comportamiento. Java determina qué método ejecutar cuando el programa está corriendo, según el tipo real del objeto.
Ejemplo: Animales con Sonidos
Se creará una clase padre Animal
y dos clases hijas, Perro
y Gato
. Cada una tendrá un método hablar
, pero con un comportamiento específico.
Código
// Clase padre
public class Animal {
public void hablar() {
System.out.println("Soy un animal y emito un sonido.");
}
}
// Clase hija: Perro
class Perro extends Animal {
@Override
public void hablar() {
System.out.println("Guau, guau");
}
}
// Clase hija: Gato
class Gato extends Animal {
@Override
public void hablar() {
System.out.println("Miau, miau");
}
}
// Clase principal para probar
class Main {
public static void main(String[] args) {
// Creación de objetos
Animal animalGenerico = new Animal();
Animal perro = new Perro();
Animal gato = new Gato();
// Ejecución del método hablar
System.out.println("Animal genérico:");
animalGenerico.hablar();
System.out.println("Perro:");
perro.hablar();
System.out.println("Gato:");
gato.hablar();
}
}
Salida
Animal genérico:
Soy un animal y emito un sonido.
Perro:
Guau, guau
Gato:
Miau, miau
Explicación Paso a Paso
- Clase Padre
Animal
: -
Define el método
hablar
con un mensaje genérico. -
Clases Hijas
Perro
yGato
: - Heredan de
Animal
con la palabraextends
. - Cada una redefine
hablar
:Perro
imprime "Guau, guau".Gato
imprime "Miau, miau".
-
La anotación
@Override
indica que se está reemplazando el método del padre. -
Funcionamiento del Polimorfismo:
- En
main
, se crean tres objetos:animalGenerico
es unAnimal
.perro
es unPerro
, pero se almacena comoAnimal
.gato
es unGato
, pero se almacena comoAnimal
.
-
Al llamar
hablar
, Java verifica el tipo real del objeto (Animal
,Perro
oGato
) y ejecuta el método correspondiente. -
Relación con el Polimorfismo:
- El método
hablar
tiene un solo nombre, pero su resultado varía según el objeto. Esto permite trabajar con objetos genéricos (Animal
) y obtener comportamientos específicos.
Representación Visual
Estructura de clases:
Animal
/ \
Perro Gato
hablar
genérico.
- Perro: hablar
→ "Guau, guau".
- Gato: hablar
→ "Miau, miau".
Actividad Guiada: Aplicando Polimorfismo en Java
Esta actividad tiene como objetivo ayudarte a comprender y practicar el polimorfismo en Java mediante un ejercicio estructurado. Crearás un programa que implementa sobrecarga de métodos (polimorfismo en tiempo de compilación) y sobreescritura de métodos (polimorfismo en tiempo de ejecución). Está diseñada para principiantes, con pasos claros, plantillas de código y explicaciones detalladas. Al finalizar, tendrás un programa funcional que aplica ambos tipos de polimorfismo.
Parte 1: Configuración Inicial
Instrucciones
Crea un proyecto Java con estas clases:
- Producto
: Clase padre con un método para describir un producto genérico.
- Libro
y Electronico
: Clases hijas que heredan de Producto
.
- Tienda
: Clase para manejar descuentos.
- Main
: Clase para ejecutar pruebas.
Código Inicial
Archivo Producto.java
:
public class Producto {
public String describir() {
return "Este es un producto genérico.";
}
}
Tarea
- Crea las clases
Libro
yElectronico
, haciendo que hereden deProducto
. No modifiques el métododescribir
por ahora. - Crea la clase
Tienda
vacía. - Crea la clase
Main
con un métodomain
vacío.
Plantilla para Libro
public class Libro extends Producto {
// No agregues código aquí todavía
}
Plantilla para Main
public class Main {
public static void main(String[] args) {
// Las pruebas se agregarán después
}
}
Verificación
- Confirma que los archivos compilen sin errores. No ejecutarán nada aún, pero las clases deben estar definidas correctamente.
Explicación
Producto
actúa como clase base paraLibro
yElectronico
.- La herencia (
extends
) permitirá a las clases hijas modificar el métododescribir
más adelante. Tienda
yMain
se usarán en las siguientes partes.
Parte 2: Sobrecarga de Métodos en la Clase Tienda
Instrucciones
En la clase Tienda
, implementa métodos sobrecargados llamados calcularDescuento
para tres casos:
- Descuento fijo del 10% sobre un precio.
- Descuento personalizado basado en un porcentaje.
- Descuento según el nivel del precio (20% si es alto, 5% si no).
Código Inicial
Archivo Tienda.java
:
public class Tienda {
// Aquí se definirán los métodos calcularDescuento
}
Tarea
Completa Tienda
con estos métodos:
1. calcularDescuento(double precio)
: Devuelve el precio con un 10% de descuento.
2. calcularDescuento(double precio, double porcentaje)
: Aplica un porcentaje de descuento específico.
3. calcularDescuento(double precio, boolean esPrecioAlto)
: Aplica un 20% de descuento si esPrecioAlto
es true
, o un 5% si es false
.
Plantilla
public class Tienda {
// Método 1: Descuento fijo del 10%
public double calcularDescuento(double precio) {
// TODO: Implementar descuento del 10%
return 0;
}
// Método 2: Descuento personalizado
public double calcularDescuento(double precio, double porcentaje) {
// TODO: Implementar descuento según porcentaje
return 0;
}
// Método 3: Descuento según precio alto
public double calcularDescuento(double precio, boolean esPrecioAlto) {
// TODO: Implementar 20% o 5% según esPrecioAlto
return 0;
}
}
Verificación
Actualiza Main
para probar los métodos:
public class Main {
public static void main(String[] args) {
Tienda tienda = new Tienda();
// Prueba método 1
System.out.println("Precio 100 con 10% descuento: " + tienda.calcularDescuento(100));
// Prueba método 2
System.out.println("Precio 100 con 25% descuento: " + tienda.calcularDescuento(100, 25));
// Prueba método 3
System.out.println("Precio 200 (alto) con descuento: " + tienda.calcularDescuento(200, true));
System.out.println("Precio 50 (normal) con descuento: " + tienda.calcularDescuento(50, false));
}
}
Salida Esperada
Precio 100 con 10% descuento: 90.0
Precio 100 con 25% descuento: 75.0
Precio 200 (alto) con descuento: 160.0
Precio 50 (normal) con descuento: 47.5
Explicación
- Sobrecarga: Los métodos
calcularDescuento
comparten el mismo nombre pero tienen diferentes parámetros. - Polimorfismo en tiempo de compilación: Java selecciona el método correcto según los argumentos al compilar.
- Esto permite usar un solo nombre para diferentes cálculos, simplificando el código.
Parte 3: Sobreescritura de Métodos en la Jerarquía de Productos
Instrucciones
Modifica Libro
y Electronico
para que sobreescriban el método describir
de Producto
. Cada clase debe devolver una descripción única:
- Producto
: Descripción genérica.
- Libro
: Indica que es un libro con páginas.
- Electronico
: Menciona que es un dispositivo con batería.
Código Inicial
Ya tienes Producto.java
:
public class Producto {
public String describir() {
return "Este es un producto genérico.";
}
}
Tarea
- En
Libro
, sobreescribedescribir
para que devuelva: "Este es un libro con páginas interesantes." - En
Electronico
, sobreescribedescribir
para que devuelva: "Este es un dispositivo electrónico con batería."
Plantilla para Libro
public class Libro extends Producto {
// TODO: Sobreescribir describir
}
Plantilla para Electronico
public class Electronico extends Producto {
// TODO: Sobreescribir describir
}
Verificación
Actualiza Main
para probar la jerarquía:
public class Main {
public static void main(String[] args) {
Tienda tienda = new Tienda();
System.out.println("Precio 100 con 10% descuento: " + tienda.calcularDescuento(100));
System.out.println("Precio 100 con 25% descuento: " + tienda.calcularDescuento(100, 25));
System.out.println("Precio 200 (alto) con descuento: " + tienda.calcularDescuento(200, true));
System.out.println("Precio 50 (normal) con descuento: " + tienda.calcularDescuento(50, false));
System.out.println("\nDescripciones de productos:");
Producto producto = new Producto();
Producto libro = new Libro();
Producto electronico = new Electronico();
System.out.println("Producto genérico: " + producto.describir());
System.out.println("Libro: " + libro.describir());
System.out.println("Electrónico: " + electronico.describir());
}
}
Salida Esperada
Precio 100 con 10% descuento: 90.0
Precio 100 con 25% descuento: 75.0
Precio 200 (alto) con descuento: 160.0
Precio 50 (normal) con descuento: 47.5
Descripciones de productos:
Producto genérico: Este es un producto genérico.
Libro: Este es un libro con páginas interesantes.
Electrónico: Este es un dispositivo electrónico con batería.
Explicación
- Sobreescritura:
Libro
yElectronico
redefinendescribir
con mensajes específicos. - Polimorfismo en tiempo de ejecución: Java selecciona el método correcto según el tipo real del objeto, aunque la variable sea de tipo
Producto
. - Esto permite manejar objetos de forma genérica con comportamientos específicos.
Código Completo
Archivo completo para referencia:
// Producto.java
public class Producto {
public String describir() {
return "Este es un producto genérico.";
}
}
// Libro.java
public class Libro extends Producto {
@Override
public String describir() {
return "Este es un libro con páginas interesantes.";
}
}
// Electronico.java
public class Electronico extends Producto {
@Override
public String describir() {
return "Este es un dispositivo electrónico con batería.";
}
}
// Tienda.java
public class Tienda {
public double calcularDescuento(double precio) {
return precio * 0.9;
}
public double calcularDescuento(double precio, double porcentaje) {
return precio * (1 - porcentaje / 100);
}
public double calcularDescuento(double precio, boolean esPrecioAlto) {
if (esPrecioAlto) {
return precio * 0.8;
} else {
return precio * 0.95;
}
}
}
// Main.java
public class Main {
public static void main(String[] args) {
Tienda tienda = new Tienda();
System.out.println("Precio 100 con 10% descuento: " + tienda.calcularDescuento(100));
System.out.println("Precio 100 con 25% descuento: " + tienda.calcularDescuento(100, 25));
System.out.println("Precio 200 (alto) con descuento: " + tienda.calcularDescuento(200, true));
System.out.println("Precio 50 (normal) con descuento: " + tienda.calcularDescuento(50, false));
System.out.println("\nDescripciones de productos:");
Producto producto = new Producto();
Producto libro = new Libro();
Producto electronico = new Electronico();
System.out.println("Producto genérico: " + producto.describir());
System.out.println("Libro: " + libro.describir());
System.out.println("Electrónico: " + electronico.describir());
}
}