Genéricos y Collections Framework
Un array de Object permite mezclar cualquier tipo, pero desplaza el error hasta la ejecución:
Object[] datos = {"Ana", 42};
String nombre = (String) datos[1]; // Compila, pero falla al ejecutarse.
El cast promete algo que el array no garantiza. Los genéricos permiten expresar el tipo aceptado y hacer que el compilador proteja esa decisión.
Un tipo como parámetro
List<String> nombres = new ArrayList<>();
nombres.add("Ana");
String nombre = nombres.get(0);
String es el parámetro de tipo. La lista solo acepta cadenas y get devuelve una cadena sin cast. List<String> y List<Integer> son tipos distintos para el compilador.
Aquí usamos List solo para observar el modelo; sus operaciones se desarrollarán en JV3-02.
Seguridad en compilación
List<String> nombres = new ArrayList<>();
nombres.add("Leo");
// nombres.add(10); // Error de compilación.
El genérico no existe solo para eliminar warnings: documenta una garantía y evita que entren valores incompatibles.
Evita los raw types:
List lista = new ArrayList(); // Pierde la garantía de tipo.
Primitivas y wrappers
Los parámetros genéricos requieren tipos de referencia. Por eso List<int> no es válido; se utiliza List<Integer>. Las correspondencias más habituales son:
int→Integerdouble→Doubleboolean→Boolean
Java convierte automáticamente entre ambos cuando resulta posible:
List<Integer> cantidades = new ArrayList<>();
cantidades.add(3); // autoboxing: int → Integer
int primera = cantidades.get(0); // unboxing: Integer → int
El valor wrapper puede ser null; al hacer unboxing de null se produce un error. La comodidad no elimina esa diferencia.
El mapa mental del framework
Collections Framework reúne interfaces y clases para organizar grupos de datos:
Collection
├── List → secuencia ordenada, admite duplicados
├── Set → elementos únicos
└── Queue → elementos pendientes según una política
Map → asociaciones clave → valor
Map no extiende Collection: no representa una colección de elementos aislados, sino asociaciones. Queue completa el mapa mental, pero no se desarrolla en este curso.
Interfaz frente a implementación
List<String> nombres = new ArrayList<>();
Listexpresa las operaciones que necesita el código.ArrayListes la implementación concreta que crea el objeto.
Programar contra la interfaz reduce la dependencia de una implementación. No significa que la implementación sea irrelevante: hay que elegir una que cumpla las necesidades.
Criterio inicial de elección
- Necesitas posición, orden y duplicados:
List. - Necesitas unicidad:
Set. - Necesitas buscar mediante una clave:
Map. - Conoces un tamaño fijo y quieres una estructura sencilla: un array todavía puede ser adecuado.
Ejemplo completo
import java.util.ArrayList;
import java.util.List;
public class GenericosDemo {
public static void main(String[] args) {
List<String> nombres = new ArrayList<>();
nombres.add("Ana");
nombres.add("Leo");
List<Integer> puntos = new ArrayList<>();
puntos.add(10);
puntos.add(15);
int total = puntos.get(0) + puntos.get(1);
System.out.println(nombres.get(0));
System.out.println(total);
}
}
Errores habituales
- Usar
Listsin parámetro de tipo. - Intentar declarar
List<int>. - Introducir casts para compensar un diseño sin tipos.
- Confundir
Listcon el objeto concretoArrayList. - Creer que
Mapforma parte deCollection.
Practica: inventario tipado
Enunciado y criterios
Crea una lista de códigos String y otra de cantidades Integer. Añade dos valores a cada una y muestra el primer código junto con su cantidad. No uses raw types ni casts.
Solución
import java.util.ArrayList;
import java.util.List;
public class InventarioTipado {
public static void main(String[] args) {
List<String> codigos = new ArrayList<>();
List<Integer> cantidades = new ArrayList<>();
codigos.add("T-01");
codigos.add("R-02");
cantidades.add(4);
cantidades.add(7);
System.out.println(codigos.get(0) + ": " + cantidades.get(0));
}
}
Cada lista ofrece una garantía distinta. En la siguiente unidad aprenderemos a modificar y recorrer secuencias dinámicas con List.