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 adelante. 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 provoca duplicación de código y dolores de cabeza a la hora de mantenerlo.
Los genéricos resuelven este problema al permitirte escribir clases y métodos que funcionan con cualquier tipo, sin dejar de mantener la seguridad de tipos. Defines un marcador de posición (llamado parámetro de tipo) que se sustituye por un tipo real cuando usas la clase:
// Sin genéricos: usa Object y requiere una conversión de tipos
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // Se necesita una conversión de tipos manual
// Con genéricos: seguridad de tipos, sin conversión de tipos
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0); // No se necesita conversión de tiposLos corchetes angulares <T> indican un parámetro de tipo. Cuando escribes List<String>, le indicas al compilador que esta lista solo contendrá cadenas. Si intentas añadir un entero, el compilador detecta el error de inmediato en lugar de que se produzca un fallo durante la ejecución.
Los genéricos ofrecen dos beneficios clave: eliminan la necesidad de conversiones de tipo explícitas y detectan errores de tipo en tiempo de compilación en lugar de en tiempo de ejecución. Esto hace que tu código sea más seguro y claro. En las siguientes lecciones, exploraremos cómo crear tus propias clases y métodos genéricos.
Desafío
FácilVamos a crear un sistema de almacenamiento sencillo que demuestre el poder de los genéricos comparando la forma antigua (usando Object con conversión de tipos) con la 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 privadocontentde tipoObject. Incluye un constructor que acepte un Object y lo almacene, y un métodogetContent()que devuelva el Object. Este enfoque funciona, pero requiere convertir el tipo al recuperar 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 tipoTen su declaración. Necesita un campo privadocontentde tipoT, un constructor que acepte un valorTy un métodogetContent()que devuelva el tipoT. ¡No se necesita ninguna conversión de tipo 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 convirtiendo el tipo 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 convertir el tipo e imprime:GenericBox (no cast): [value]Después, muestra cómo funcionan los genéricos con números enteros: crea un
GenericBox<Integer>que almacene el número, recupéralo e imprime:GenericBox Integer: [value]Por último, imprime una línea en blanco seguida de:
Type safety: Generics catch errors at compile time!
Recibirás dos entradas en este orden: una palabra (String) y un número (entero como String, que analizarás).
Observa la diferencia clave: con OldBox, debes convertir el tipo con (String) box.getContent() y esperar haber obtenido el tipo correcto. Con GenericBox, el compilador conoce el tipo y no hace falta convertirlo. Esta es la ventaja fundamental de los genéricos: ¡seguridad de tipos sin las complicaciones de las conversiones!
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: Crea un OldBox que almacene la palabra
// Recupera el valor con una conversión a String e imprime: "OldBox (with cast): [value]"
// TODO: Crea un GenericBox<String> que almacene la palabra
// Recupera el valor sin conversión de tipo e imprime: "GenericBox (no cast): [value]"
// TODO: Crea un GenericBox<Integer> que almacene el número
// Recupera el valor e imprime: "GenericBox Integer: [value]"
// TODO: Imprime una línea en blanco y luego imprime:
// "Type safety: Generics catch errors at compile time!"
}
}
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
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón IteratorPractica por tu cuenta: Compilador de Java online