Generics en Java
¿Qué son los Generics en Java?
Los Generics (o tipos genéricos) en Java permiten crear clases, interfaces y métodos que
pueden operar sobre diferentes tipos de datos, pero sin la necesidad de definir esos tipos
explícitamente en el momento de escribir el código. En vez de especificar un tipo concreto,
se usa un parámetro de tipo que se define en tiempo de compilación. Esto proporciona
tipado estático y reutilización de código.
Antes de que existieran los Generics, las colecciones como List o ArrayList contenían
objetos de tipo Object, lo que implicaba que al extraer un objeto de estas colecciones era
necesario hacer casts manuales al tipo correcto. Esto era propenso a errores, ya que en
tiempo de ejecución podía fallar si los tipos no coincidían.
Los Generics ayudan a resolver este problema proporcionando tipos seguros en tiempo de
compilación.
Ventajas de los Generics:
1. Reutilización de código: Puedes escribir clases y métodos que funcionan con
cualquier tipo de datos.
2. Seguridad en tiempo de compilación: Al usar Generics, los errores de tipo se
detectan en tiempo de compilación, evitando errores comunes que ocurren en
tiempo de ejecución.
3. Evitan los casts innecesarios: Con Generics, no necesitas hacer cast al extraer
elementos de una colección.
4. Facilitan la lectura y mantenimiento del código: El código genérico es más limpio
y más fácil de entender, ya que no hay necesidad de transformar tipos.
Sintaxis básica de los Generics:
class NombreClase<T> {
// T es el parámetro de tipo genérico
private T valor;
public void setValor(T valor) {
this.valor = valor;
}
public T getValor() {
return valor;
}
}
En este ejemplo, T es un parámetro de tipo que será sustituido por un tipo concreto en
tiempo de compilación (como String, Integer, Persona, etc.).
Uso de Generics en Colecciones
Las colecciones en Java, como ArrayList, HashMap, y HashSet, hacen un uso intensivo
de Generics. Vamos a ver ejemplos con varias colecciones.
Ejemplo 1: ArrayList con Generics
import java.util.ArrayList;
public class EjemploArrayList {
public static void main(String[] args) {
// ArrayList que solo acepta Strings
ArrayList<String> lista = new ArrayList<>();
// Agregar elementos a la lista
lista.add("Hola");
lista.add("Mundo");
// No es necesario hacer cast
String palabra = lista.get(0);
// Mostrar elementos
for (String s : lista) {
System.out.println(s);
}
}
}
Sin Generics: Antes de que existieran, habrías tenido que declarar la lista como
ArrayList y hacer un cast cada vez que sacaras un elemento.
Ejemplo 2: HashMap con Generics
import java.util.HashMap;
public class EjemploHashMap {
public static void main(String[] args) {
// HashMap que utiliza Integer como clave y String como valor
HashMap<Integer, String> mapa = new HashMap<>();
// Agregar elementos al mapa
mapa.put(1, "Uno");
mapa.put(2, "Dos");
mapa.put(3, "Tres");
// Obtener un valor usando la clave
String valor = mapa.get(1);
// Mostrar todos los pares clave-valor
for (Integer clave : mapa.keySet()) {
System.out.println("Clave: " + clave + ", Valor: " +
mapa.get(clave));
}
}
}
En este ejemplo, HashMap<Integer, String> nos asegura que las claves serán
siempre de tipo Integer y los valores de tipo String. Si intentamos agregar un valor de
otro tipo, obtendremos un error de compilación.
Ejemplo 3: Clase genérica personalizada
Puedes crear tu propia clase genérica para reutilizarla con diferentes tipos:
class Caja<T> {
private T contenido;
public void guardar(T contenido) {
this.contenido = contenido;
}
public T obtener() {
return contenido;
}
}
public class EjemploClaseGenerica {
public static void main(String[] args) {
// Caja que contiene un String
Caja<String> cajaString = new Caja<>();
cajaString.guardar("Hola Generics");
System.out.println("Contenido de cajaString: " +
cajaString.obtener());
// Caja que contiene un Integer
Caja<Integer> cajaInteger = new Caja<>();
cajaInteger.guardar(123);
System.out.println("Contenido de cajaInteger: " +
cajaInteger.obtener());
}
}
Aquí, Caja<T> es una clase genérica que puede almacenar cualquier tipo de objeto. Este
ejemplo demuestra cómo puedes reutilizar la clase Caja para trabajar con String,
Integer o cualquier otro tipo.
Wildcards en Generics
En Java, puedes usar comodines (wildcards) en generics para indicar que el tipo puede ser
de varios tipos compatibles.
Wildcards con el símbolo ?
? indica un tipo desconocido. Es útil cuando no te importa el tipo exacto, pero sí sabes que
trabajarás con algún tipo.
Ejemplo de wildcard con Listas:
import java.util.List;
import java.util.ArrayList;
public class EjemploWildcards {
public static void mostrarLista(List<?> lista) {
for (Object elemento : lista) {
System.out.println(elemento);
}
}
public static void main(String[] args) {
List<String> listaString = new ArrayList<>();
listaString.add("Hola");
listaString.add("Generics");
List<Integer> listaInteger = new ArrayList<>();
listaInteger.add(100);
listaInteger.add(200);
// Podemos pasar cualquier tipo de lista
mostrarLista(listaString);
mostrarLista(listaInteger);
}
}
En este ejemplo, List<?> acepta listas de cualquier tipo, ya sea una lista de String,
Integer, etc.
Generics con restricciones de tipo (Bounded Types)
Puedes restringir un parámetro genérico para que solo acepte tipos que extienden de una
clase o implementan una interfaz. Para eso se usa extends.
Ejemplo con Bounded Types:
class CajaNumerica<T extends Number> {
private T numero;
public CajaNumerica(T numero) {
this.numero = numero;
}
public T obtenerNumero() {
return numero;
}
}
public class EjemploBoundedTypes {
public static void main(String[] args) {
CajaNumerica<Integer> cajaEntero = new CajaNumerica<>(100);
CajaNumerica<Double> cajaDouble = new CajaNumerica<>(99.99);
System.out.println("Entero: " + cajaEntero.obtenerNumero());
System.out.println("Double: " + cajaDouble.obtenerNumero());
// CajaNumerica<String> cajaString = new
CajaNumerica<>("Texto"); // Error de compilación
}
}
Aquí, CajaNumerica<T extends Number> solo acepta tipos que hereden de Number
(como Integer, Double, etc.). Si intentas usar un tipo que no sea un número, como
String, obtendrás un error de compilación.