Laboratorio 05 — Polimorfismo en Java - jorgealbertopineda78-del/java GitHub Wiki
Unidad: 5 — Polimorfismo
Duración estimada: 90 minutos
Nivel: Intermedio
En el Lab04 terminaste con este bloque en Main:
Personaje[] equipo = { guerrero, mago, arquero };
for (Personaje p : equipo) {
p.atacar(); // ¿por qué cada uno responde diferente?
}Esa pregunta es exactamente lo que responde este laboratorio.
Bienvenido al polimorfismo.
Comprender e implementar polimorfismo en Java a través de:
- Referencias de tipo padre apuntando a objetos de tipo hijo
- Despacho dinámico de métodos en tiempo de ejecución
- Sobrecarga de métodos (
overloading) como forma estática de polimorfismo - Uso de
instanceofpara identificar el tipo real de un objeto
El videojuego RPG del Lab04 ha crecido. Ahora el sistema necesita gestionar equipos mixtos de personajes sin saber de antemano qué tipo exacto es cada uno. El motor del juego debe poder:
- Procesar una batalla con cualquier combinación de personajes
- Calcular el daño de ataque de diferentes maneras según el tipo
- Registrar eventos de combate en un historial
Tu tarea es ampliar la jerarquía existente aplicando polimorfismo real.
┌─────────────────────────────────┐
│ Personaje │
│ (del Lab04) │
├─────────────────────────────────┤
│ + atacar() : void │
│ + defender() : void │
│ + calcularDanio() : int │ ← NUEVO
│ + toString() : String │
└──────────────┬──────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
Guerrero Mago Arquero
calcularDanio() calcularDanio() calcularDanio()
(fuerza * nivel) (mana * nivel) (precision * flechas)
┌─────────────────────────────────┐
│ GestorBatalla │ ← NUEVA CLASE
├─────────────────────────────────┤
│ - historial : ArrayList<String> │
├─────────────────────────────────┤
│ + ejecutarAtaque(Personaje) │ ← sobrecarga
│ + ejecutarAtaque(Personaje, │
│ Personaje) │ ← sobrecarga
│ + ejecutarAtaque(Personaje[]) │ ← sobrecarga
│ + mostrarHistorial() │
└─────────────────────────────────┘
Reutiliza o recrea las clases Personaje, Guerrero, Mago y Arquero del Lab04.
Agrega el método calcularDanio() en cada clase:
En Personaje (método base):
public int calcularDanio() {
return getNivel() * 10; // daño base según nivel
}En Guerrero — sobreescribe calcularDanio():
- Retorna
fuerza * getNivel()
En Mago — sobreescribe calcularDanio():
- Retorna
mana * getNivel()
En Arquero — sobreescribe calcularDanio():
- Retorna
precision * flechasDisponibles(o los atributos que hayas definido)
💡 Esto es polimorfismo: el mismo método
calcularDanio()produce resultados distintos según el tipo real del objeto, aunque la variable sea de tipoPersonaje.
Crea GestorBatalla.java con los siguientes métodos sobrecargados:
// Versión 1: un solo atacante
public void ejecutarAtaque(Personaje atacante)
// Versión 2: atacante vs defensor
public void ejecutarAtaque(Personaje atacante, Personaje defensor)
// Versión 3: todo un equipo ataca
public void ejecutarAtaque(Personaje[] equipo)Cada versión debe:
- Calcular el daño con
atacante.calcularDanio() - Llamar a
atacante.atacar() - Si hay defensor, llamar a
defensor.recibirDanio(danio)y adefensor.defender() - Registrar el evento en el
historial(unArrayList<String>)
Agrega también:
-
mostrarHistorial()— imprime todos los eventos registrados -
limpiarHistorial()— vacía el historial
💡 Esto es sobrecarga: mismo nombre de método (
ejecutarAtaque), diferente firma (distintos parámetros). El compilador elige cuál usar según los argumentos.
En Main.java, demuestra el polimorfismo de las siguientes formas:
3a — Variable de tipo padre apunta a objeto hijo:
Personaje p1 = new Guerrero("Thorin", 5, 200, 85, "Cota de Malla");
Personaje p2 = new Mago("Gandalf", 8, 120, 150, "Fuego");
Personaje p3 = new Arquero("Legolas", 6, 150, "Arco Largo", 30, 95);
System.out.println(p1.calcularDanio()); // ejecuta el de Guerrero
System.out.println(p2.calcularDanio()); // ejecuta el de Mago
System.out.println(p3.calcularDanio()); // ejecuta el de Arquero3b — Arreglo polimórfico:
Personaje[] equipo = { p1, p2, p3 };
for (Personaje p : equipo) {
System.out.println(p.getNombre() + " daño: " + p.calcularDanio());
}3c — Usar GestorBatalla con las tres sobrecargas:
GestorBatalla gestor = new GestorBatalla();
gestor.ejecutarAtaque(p1); // versión 1 persona
gestor.ejecutarAtaque(p2, p3); // versión atacante vs defensor
gestor.ejecutarAtaque(equipo); // versión equipo completo
gestor.mostrarHistorial();3d — Identificar tipo real con instanceof:
for (Personaje p : equipo) {
if (p instanceof Guerrero) {
System.out.println(p.getNombre() + " es un Guerrero.");
} else if (p instanceof Mago) {
System.out.println(p.getNombre() + " es un Mago.");
} else if (p instanceof Arquero) {
System.out.println(p.getNombre() + " es un Arquero.");
}
}Agrega en Personaje (o en cualquier subclase) al menos dos versiones sobrecargadas de un método nuevo. Por ejemplo:
// En Personaje:
public void mostrarEstado() // imprime info básica
public void mostrarEstado(boolean detallado) // imprime info completa
public void mostrarEstado(String formato) // imprime con prefijo personalizadoO en una subclase:
// En Guerrero:
public void entrenar() // sube fuerza en 5
public void entrenar(int sesiones) // sube fuerza en 5 * sesiones
public void entrenar(int sesiones, boolean intensivo) // intensivo duplica el efectoDemuestra las tres versiones en Main.
POLIMORFISMO (Override) vs SOBRECARGA (Overload)
─────────────────────────────────────────────────────────
Mismo método Mismo nombre
Diferente clase Diferente firma (parámetros)
Se resuelve en EJECUCIÓN Se resuelve en COMPILACIÓN
Requiere herencia No requiere herencia
=== RPG — Demostración de Polimorfismo ===
-- calcularDanio() por tipo --
Thorin (Guerrero) daño: 425 ← fuerza(85) * nivel(5)
Gandalf (Mago) daño: 1200 ← mana(150) * nivel(8)
Legolas (Arquero) daño: 2850 ← precision(95) * flechas(30)
-- Arreglo polimórfico --
[Thorin] ataca con un golpe básico.
¡Thorin golpea con su espada causando 85 de daño!
[Gandalf] ataca con un golpe básico.
¡Gandalf lanza una bola de fuego causando 150 de daño mágico!
[Legolas] ataca con un golpe básico.
Legolas dispara una flecha. Flechas restantes: 29
-- GestorBatalla --
[BATALLA] Thorin ataca solo → daño: 425
[BATALLA] Gandalf ataca a Legolas → daño: 1200
Legolas recibe 1200 puntos de daño. Vida restante: 0
Legolas ha sido derrotado.
[BATALLA] Equipo completo ataca → 3 personajes
-- Historial --
1. Thorin atacó solo (daño: 425)
2. Gandalf atacó a Legolas (daño: 1200) - Legolas derrotado
3. Ataque en equipo: Thorin, Gandalf, Legolas
-- instanceof --
Thorin es un Guerrero.
Gandalf es un Mago.
Legolas es un Arquero.
| Criterio | Puntos |
|---|---|
calcularDanio() sobreescrito correctamente en las 3 subclases |
20 |
GestorBatalla con las 3 sobrecargas de ejecutarAtaque()
|
20 |
| Historial funciona y registra eventos correctamente | 10 |
Demostración con arreglo polimórfico Personaje[]
|
15 |
Uso correcto de instanceof para identificar tipos |
10 |
| Sobrecarga adicional propia (Parte 4) con 3 versiones | 15 |
Main integra todo y la salida es coherente |
10 |
| Total | 100 |
Responde en respuestas.txt:
- ¿Cuál es la diferencia entre sobreescritura (
@Override) y sobrecarga (overload)? Da un ejemplo de cada uno tomado de este laboratorio. - Si tienes
Personaje p = new Mago(...)y llamasp.calcularDanio(), ¿Java usa el método dePersonajeo el deMago? ¿Por qué? - ¿Para qué sirve
instanceof? ¿En qué situaciones lo usarías en un sistema real?
Sube al repositorio en la carpeta lab05_polimorfismo:
lab05_polimorfismo/
├── Combatiente.java
├── Personaje.java
├── Guerrero.java
├── Mago.java
├── Arquero.java
├── GestorBatalla.java
└── Main.java
Laboratorio preparado para el curso de Programación Orientada a Objetos — Ago-Dic 2025