Generics en Java: para qué sirven y cómo usarlos
Generics (genéricos) — una de las características clave de Java, introducida en la versión J2SE 5.0. Permiten crear clases, interfaces y métodos que trabajan con tipos de datos especificados en el momento de la llamada. En lugar de escribir implementaciones separadas para Integer, String y Double, escribes un único código universal. En este artículo analizaremos para qué sirven los Generics en Java, cómo usarlos en la práctica y qué trampas debes conocer.
¿Para qué sirven los Generics en Java?
Antes de la aparición de los Generics, los desarrolladores usaban Object para almacenar cualquier tipo. Esto funcionaba, pero generaba dos problemas graves:
- Falta de verificación de tipos en tiempo de compilación. Podías accidentalmente poner un
Stringen una lista en lugar de unInteger, y el error solo se manifestaba durante la ejecución del programa. - Necesidad de conversión explícita de tipos. Cada vez que extraías un elemento de una colección, requerías un cast:
(String) list.get(0). Esto hacía el código voluminoso y peligroso.
Los Generics resuelven ambos problemas. Permiten al compilador verificar la corrección de los tipos y realizar la conversión automáticamente. Veamos un ejemplo simple:
// Sin Generics (estilo antiguo)List list = new ArrayList();list.add("Hello");String s = (String) list.get(0); // se necesita conversión explícita
// Con GenericsList<String> list = new ArrayList<>();list.add("Hello");String s = list.get(0); // no se requiere conversiónEl código se volvió más seguro y legible. El error de tipo se detecta en tiempo de compilación, no en tiempo de ejecución.
Cómo usar Generics: construcciones principales
1. Clases genéricas
Puedes definir una clase que acepte uno o varios parámetros de tipo. A menudo se usa una sola letra: T (Type), E (Element), K (Key), V (Value).
public class Box<T> { private T content;
public void put(T item) { this.content = item; }
public T get() { return content; }}
// UsoBox<String> stringBox = new Box<>();stringBox.put("Java");String value = stringBox.get(); // conversión automáticaEl parámetro T se sustituye por el tipo real al crear el objeto. El compilador garantiza que en un Box<String> no se pueda poner un Integer.
2. Métodos genéricos
Los métodos también pueden ser genéricos, incluso si la clase no lo es. El parámetro de tipo se indica antes del tipo de retorno:
public class Utils { public static <T> T getMiddle(T... args) { return args[args.length / 2]; }}
// LlamadaString middle = Utils.<String>getMiddle("A", "B", "C");// o simplemente: Utils.getMiddle("A", "B", "C") — el compilador deduce el tipoLos métodos genéricos son útiles para funciones utilitarias que trabajan con diferentes tipos de datos.
3. Tipos acotados (Bounded Type Parameters)
A veces es necesario limitar qué tipos pueden ser sustituidos. Por ejemplo, para que un método funcione solo con números:
public class Calculator { public static <T extends Number> double sum(T a, T b) { return a.doubleValue() + b.doubleValue(); }}
// Funciona con Integer, Double, Float, etc.double result = Calculator.sum(5, 10); // 15.0La palabra clave extends significa que T debe ser un subtipo de la clase especificada (o implementar una interfaz).
Wildcards: tipos desconocidos
Los Wildcards (comodines) se usan cuando el tipo exacto es desconocido o no es importante. Se denotan con el signo de interrogación ?. Hay tres tipos:
Blogs
Book Recommendations
Leer también
Cómo aprobar una entrevista de Go: mejores preguntas y respuestas
Trabajo con JSON en Kotlin: análisis y serialización para principiantes
Patrones de diseño en Rust: técnicas avanzadas con ejemplos de código
Parseo en Swift: creamos un parser de datos en 30 minutos
Generics en C++: para qué sirven y cómo usar plantillas