TutorJava Nivel Básico: tutorial completo Java 21

Los genéricos (Generics) permiten escribir clases y métodos que funcionan con cualquier tipo, con verificación en tiempo de compilación. Eliminan los casts explícitos y detectan errores de tipo antes de ejecutar el programa.

Por qué existen los genéricos

Antes de Java 5, las colecciones trabajaban con Object. Esto obligaba a hacer casts y permitía mezclar tipos sin que el compilador protestara:

// Sin genéricos (Java pre-5) — propenso a ClassCastException en runtime
List lista = new ArrayList();
lista.add("Hola");
lista.add(42);               // el compilador no se queja
String s = (String) lista.get(1);  // ClassCastException en ejecución!

// Con genéricos (Java 5+) — el compilador detecta el error
List lista = new ArrayList<>();
lista.add("Hola");
lista.add(42);               // ERROR en compilación: incompatible types
String s = lista.get(0);     // sin cast necesario

Clases genéricas

public class Caja {
    private T contenido;

    public Caja(T contenido) {
        this.contenido = contenido;
    }

    public T abrir() { return contenido; }
    public void poner(T nuevo) { this.contenido = nuevo; }

    @Override
    public String toString() {
        return "Caja[" + contenido + "]";
    }
}

Caja cajaTexto = new Caja<>("Hola Java");
Caja cajaNumero = new Caja<>(42);

String texto = cajaTexto.abrir(); // sin cast
Integer num = cajaNumero.abrir();

Métodos genéricos

// Método que intercambia dos elementos de un array
public static  void intercambiar(T[] arr, int i, int j) {
    T temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

String[] palabras = {"Hola", "Mundo", "Java"};
intercambiar(palabras, 0, 2);
// palabras: ["Java", "Mundo", "Hola"]

// Método que devuelve el máximo de dos comparables
public static > T maximo(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

System.out.println(maximo(10, 20));      // 20
System.out.println(maximo("Ana", "Luis")); // Luis

Wildcards: ? extends y ? super

// ? extends T: lee de una colección de T o subtipo (covariante)
public static double sumarNumeros(List lista) {
    double total = 0;
    for (Number n : lista) total += n.doubleValue();
    return total;
}

List enteros = List.of(1, 2, 3);
List decimales = List.of(1.5, 2.5);
sumarNumeros(enteros);   // OK
sumarNumeros(decimales); // OK

// ? super T: escribe en una colección de T o supertipo (contravariante)
public static void llenar(List lista, int cantidad) {
    for (int i = 0; i < cantidad; i++) lista.add(i);
}

Borrado de tipos (Type Erasure)

Los genéricos en Java son solo en tiempo de compilación. En tiempo de ejecución, la JVM trabaja con Object (o el bound si hay extends). Esto tiene consecuencias: no puedes crear arrays de genéricos (new T[10] no compila), ni hacer instanceof sobre tipos genéricos (if (obj instanceof List<String>) no compila).

Records (Java 16+): la alternativa moderna a clases de datos

Los records son clases inmutables de datos que generan automáticamente constructor, getters, equals, hashCode y toString:

// Definición de un record
public record Punto(double x, double y) {
    // Método de instancia personalizado
    public double distanciaAlOrigen() {
        return Math.sqrt(x * x + y * y);
    }
}

// Uso
Punto p = new Punto(3.0, 4.0);
System.out.println(p.x());          // 3.0  (getter generado)
System.out.println(p.y());          // 4.0
System.out.println(p.distanciaAlOrigen()); // 5.0
System.out.println(p);              // Punto[x=3.0, y=4.0]

Punto p2 = new Punto(3.0, 4.0);
System.out.println(p.equals(p2));   // true (equals generado)

Los records son perfectos para DTOs (Data Transfer Objects), resultados de consultas, eventos y cualquier clase cuyo propósito es solo transportar datos.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP