Generics en Java: para qué sirven y cómo usarlos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

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 String en una lista en lugar de un Integer, 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ón


El 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ática


El 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 tipo


Los 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.0


La 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: