Conceptos Avanzados de Programación Orientada a Objetos en Java: Herencia, Polimorfismo y Colecciones
Enviado por Chuletator online y clasificado en Informática y Telecomunicaciones
Escrito el en
español con un tamaño de 7,99 KB
Conceptos Avanzados de Programación Orientada a Objetos (POO) en Java
1. Clase Padre (Abstracta)
La clase abstracta define una estructura común y obliga a las subclases a implementar ciertos comportamientos.
public abstract class Producto {
// PROTECTED: Para que los hijos (Pila, Pintura) accedan directamente
protected String nombre;
protected double precioBase;
public Producto(String nombre, double precioBase) {
this.nombre = nombre;
this.precioBase = precioBase;
}
// Método abstracto: Obliga a los hijos a definir su propia fórmula
public abstract double calcularPVP();
// Método común: Todos lo usan igual (reutilización)
public String getNombre() { return nombre; }
}2. Colecciones y Polimorfismo
El polimorfismo permite tratar objetos de diferentes clases (hijos) como si fueran del tipo de la clase común (padre).
Se utiliza la clase más genérica a la izquierda (ej. List o Producto):
ArrayList<Producto> inventario = new ArrayList<>();Uso del Polimorfismo
Se añaden instancias de diferentes clases:
inventario.add(new Pila("AAA", 5.0, "Alcalina"));
inventario.add(new Pintura("Blanca", 20.0, 5));Recorrido y Despacho Dinámico
Mediante un bucle for-each, Java determina automáticamente qué versión de calcularPVP() usar:
for (Producto p : inventario) {
// Java sabe automáticamente qué versión de calcularPVP() usar
System.out.println(p.getNombre() + " - " + p.calcularPVP());
// ⚠️ PELIGRO: Si necesitas un atributo exclusivo del hijo (ej. tipo de Pila)
if (p instanceof Pila) {
Pila laPila = (Pila) p; // Casteo explícito
System.out.println("Es pila tipo: " + laPila.getTipo());
}
}3. La palabra clave final
Se utiliza cuando un método no debe ser sobrescrito por ninguna subclase. Ejemplo:
"El cálculo del IVA es inamovible y ninguna subclase debe poder modificarlo".
public final double calcularIVA() {
return this.precio * 0.21;
}4. Ordenamiento de Colecciones
A. Orden Natural (Implementando Comparable)
Se define DENTRO de la clase Producto y establece el orden "por defecto".
public abstract class Producto implements Comparable<Producto> {
// ... atributos ...
@Override
public int compareTo(Producto otro) {
// Para Strings:
return this.referencia.compareTo(otro.referencia);
// Para Ints: return this.stock - otro.stock;
}
}
// Uso: Collections.sort(inventario);
B. Orden "a la carta" (Usando Comparator)
Se define FUERA (en el main o clase controladora) para ordenar por criterios específicos (nombre, precio, etc.).
Truco Lambda (Fácil de memorizar):
(p1, p2) -> p1.getLoQueSea().compareTo(p2.getLoQueSea())
Collections.sort(inventario, (p1, p2) -> p1.getNombre().compareTo(p2.getNombre()));5. Formateo de Salida (Tabla)
Se usa System.out.printf para alinear columnas:
System.out.printf("%\-10s %\-20s %\-5d %.2f€\n",
producto.getRef(),
producto.getNombre(),
producto.getStock(),
producto.getPrecio());6. La Trampa de Borrar en un Bucle (Uso de Iterator)
Para eliminar elementos de una colección mientras se itera, se debe usar el método remove() del iterador, no el de la lista, para evitar ConcurrentModificationException.
Iterator<Producto> it = inventario.iterator();
while (it.hasNext()) {
Producto p = it.next();
if (p.getStock() == 0) {
it.remove(); // Usar el remove del iterador, NO de la lista
}
}
Opción B (Moderna - "Pro"):
Usando expresiones lambda para eliminar condicionalmente:
// "Elimina si el stock es cero"
inventario.removeIf(p -> p.getStock() == 0);
7. Casteo Condicional Específico
Cuando se necesita acceder a métodos exclusivos de una subclase (ej. Ultramarino), se debe verificar el tipo y luego realizar el casteo (conversión de referencia).
La Solución (Código obligatorio):
for (Producto p : inventario) {
// 1. Preguntar: ¿Es este producto una instancia de Ultramarino?
if (p instanceof Ultramarino) {
// 2. Castear (Convertir la referencia): "Baja" de Producto a Ultramarino
Ultramarino u = (Ultramarino) p;
// 3. Ahora SÍ puedes usar métodos exclusivos del hijo
System.out.println("Caduca el: " + u.getCaducidad());
}
}
8. Implementación del método equals()
Es vital para el correcto funcionamiento de métodos como remove() y contains() en colecciones.
Implementación en la clase Producto (Padre)
@Override
public boolean equals(Object obj) {
// 1. Si es el mismo objeto en memoria -> true
if (this == obj) return true;
// 2. Si es null o no es un Producto -> false
if (obj == null || getClass() != obj.getClass()) return false;
// 3. Casteamos para poder comparar atributos
Producto otro = (Producto) obj;
// 4. Comparamos lo que NOSOTROS consideramos identidad (la referencia/ID)
// OJO: Usar .equals() para Strings, nunca ==
return this.referencia.equals(otro.referencia);
}
9. Estructuras de Datos Adicionales
Set (Conjuntos) para evitar duplicados
Los Sets garantizan que no habrá elementos repetidos. No guardan el orden de inserción (a menos que se use LinkedHashSet).
import java.util.HashSet;
import java.util.Set;
Set<String> correos = new HashSet<>();
correos.add("[email protected]");
boolean agregado = correos.add("[email protected]"); // Devuelve FALSE, no lo añade.
System.out.println(correos.size()); // Imprime 1, no 2.
10. Manejo de Errores con try-catch
Permite capturar excepciones (errores en tiempo de ejecución) sin que el programa se detenga.
int opcion = -1; // Valor por defecto inválido
boolean datoCorrecto = false;
while (!datoCorrecto) {
try {
System.out.print("Introduce opción: ");
// Si el usuario mete letras, aquí explota y salta al catch
opcion = Integer.parseInt(sc.nextLine());
datoCorrecto = true; // Si llega aquí, todo fue bien
} catch (NumberFormatException e) {
// Aquí capturamos el error sin que el programa se cierre
System.out.println("¡Error! Debes introducir un número entero.");
}
}
11. El "Busca-Rápido": HashMap (Diccionarios)
Permite acceder a elementos directamente mediante una clave, ofreciendo búsquedas casi instantáneas (O(1)).
import java.util.HashMap;
import java.util.Map;
// Declaración: <Clave, Valor> -> <String referencia, Producto objeto>
Map<String, Producto> inventarioMap = new HashMap<>();
// 1. AÑADIR (put en vez de add)
inventarioMap.put("REF001", new Pila("Duracell", 5.0, "AA"));
inventarioMap.put("REF002", new Ultramarino("Leche", 1.0, fCad));
// 2. BUSCAR (get) -> ¡Sin bucles for! Es instantáneo.
Producto p = inventarioMap.get("REF001");
if (p != null) {
System.out.println("Encontrado: " + p.getNombre());
} else {
System.out.println("No existe esa referencia");
}
// 3. RECORRER (Usando .values() para obtener solo los objetos)
for (Producto prod : inventarioMap.values()) {
System.out.println(prod);
}