Menu
Coddy logo textTech

Introducción a los genéricos

Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 53 de 87.

Imagina que necesitas crear una clase que almacene un valor y lo recupere más tarde. Sin genéricos, tendrías que escribir clases separadas para cada tipo: una para Integer, una para String, una para Double, y así sucesivamente. Esto conduce a la duplicación de código y a dolores de cabeza en el mantenimiento.

Los genéricos resuelven este problema al permitirte escribir clases y métodos que funcionen con cualquier tipo, manteniendo al mismo tiempo la seguridad de tipos. Defines un marcador de posición (llamado parámetro de tipo) que se reemplaza por un tipo real cuando utilizas la clase:

// Sin genéricos - usa Object, requiere conversión
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Se requiere conversión manual

// Con genéricos - seguro para tipos, sin conversión
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // No se requiere conversión

Los corchetes angulares <T> denotan un parámetro de tipo. Cuando escribes List<String>, le estás diciendo al compilador que esta lista solo contendrá cadenas. Si intentas añadir un entero, el compilador detecta el error inmediatamente en lugar de fallar en tiempo de ejecución.

Los genéricos proporcionan dos beneficios clave: eliminan la necesidad de realizar conversiones explícitas y detectan errores de tipo en tiempo de compilación en lugar de en tiempo de ejecución. Esto hace que su código sea más seguro y limpio. En las siguientes lecciones, exploraremos cómo crear sus propias clases y métodos genéricos.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de almacenamiento simple que demuestre el poder de los genéricos comparando la forma antigua (usando Object con casting) frente a la forma nueva (usando genéricos con seguridad de tipos).

Crearás tres archivos para ver la diferencia de primera mano:

  • OldBox.java: Crea una clase que almacene valores al estilo anterior a los genéricos. Tu OldBox debe tener un campo privado content de tipo Object. Incluye un constructor que acepte un Object y lo almacene, y un método getContent() que devuelva el Object. Este enfoque funciona pero requiere casting al recuperar los valores.
  • GenericBox.java: Ahora crea la versión moderna y segura en cuanto a tipos usando genéricos. Tu GenericBox debe usar un parámetro de tipo T en su declaración. Necesita un campo privado content de tipo T, un constructor que acepte un valor T, y un método getContent() que devuelva el tipo T. ¡No se necesita casting al usar esta clase!
  • Main.java: Demuestra ambos enfoques uno al lado del otro. Recibirás dos entradas: una palabra (String) y un número (entero).

    Primero, usa el enfoque antiguo: crea un OldBox que almacene la palabra, luego recupérala con un cast a String e imprime: OldBox (with cast): [value]

    A continuación, usa el enfoque genérico: crea un GenericBox<String> que almacene la misma palabra, recupérala sin casting e imprime: GenericBox (no cast): [value]

    Luego muestra los genéricos funcionando con enteros: crea un GenericBox<Integer> que almacene el número, recupéralo e imprime: GenericBox Integer: [value]

    Finalmente, imprime una línea en blanco seguida de: Type safety: Generics catch errors at compile time!

Recibirás dos entradas en orden: una palabra (String) y un número (entero como String, que deberás parsear).

Observa la diferencia clave: con OldBox, debes hacer un cast (String) box.getContent() y esperar haber acertado con el tipo. Con GenericBox, el compilador conoce el tipo y no es necesario realizar ningún casting. ¡Este es el beneficio principal de los genéricos: seguridad de tipos sin la molestia del casting!

Hoja de referencia

Los genéricos le permiten escribir clases y métodos que funcionan con cualquier tipo manteniendo la seguridad de tipos. Utilizan parámetros de tipo (marcadores de posición) que se reemplazan con tipos reales cuando se utiliza la clase.

Sin genéricos (usando Object):

List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Manual casting required

Con genéricos (seguridad de tipos):

List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // No casting needed

Creación de una clase genérica:

public class GenericBox<T> {
    private T content;
    
    public GenericBox(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

Uso de una clase genérica:

GenericBox<String> stringBox = new GenericBox<String>("Hello");
String value = stringBox.getContent();  // No casting

GenericBox<Integer> intBox = new GenericBox<Integer>(42);
Integer number = intBox.getContent();  // Type-safe

Los corchetes angulares <T> denotan un parámetro de tipo. Los beneficios incluyen: no se requiere casting explícito y los errores de tipo se detectan en tiempo de compilación en lugar de en tiempo de ejecución.

Pruébalo tú mismo

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String word = scanner.nextLine();
        int number = Integer.parseInt(scanner.nextLine());
        
        // TODO: Crear un OldBox que almacene la palabra
        // Recuperar con un cast a String e imprimir: "OldBox (with cast): [value]"
        
        // TODO: Crear un GenericBox<String> que almacene la palabra
        // Recuperar sin casting e imprimir: "GenericBox (no cast): [value]"
        
        // TODO: Crear un GenericBox<Integer> que almacene el número
        // Recuperar e imprimir: "GenericBox Integer: [value]"
        
        // TODO: Imprimir una línea en blanco, luego imprimir:
        // "Type safety: Generics catch errors at compile time!"
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos