Clases genéricas
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 54 de 87.
Ahora que entiendes por qué los genéricos son útiles, vamos a crear tu propia clase genérica. Una clase genérica declara uno o más parámetros de tipo entre corchetes angulares después del nombre de la clase, los cuales pueden utilizarse en toda la clase.
class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}El T es un parámetro de tipo: un marcador de posición que se reemplaza por un tipo real cuando creas una instancia. Puedes usar cualquier nombre, pero las letras mayúsculas individuales son convencionales: T para Tipo, E para Elemento, K para Clave, V para Valor.
Al instanciar una clase genérica, se especifica el tipo real entre paréntesis angulares:
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // No casting needed
Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();Observa el <> (operador diamante) en el lado derecho: Java infiere el tipo a partir de la declaración. También puedes usar múltiples parámetros de tipo para clases que necesiten trabajar con más de un tipo:
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> pair = new Pair<>("age", 25);Desafío
FácilVamos a construir un sistema de registro de regalos que demuestre el poder de las clases genéricas. Crearás un sistema de almacenamiento flexible que puede contener cualquier tipo de artículo de regalo, además de un registro que empareja los nombres de los regalos con sus detalles utilizando múltiples parámetros de tipo.
Organizarás tu código en tres archivos:
GiftBox.java: Crea una clase genérica que pueda envolver cualquier tipo de regalo. TuGiftBox<T>debe tener un campo privadoitemde tipoTy un campo privadowrapped(boolean) que comience comofalse. Incluye un constructor que acepte el artículo, un métodogetItem()que devuelva el artículo, un métodowrap()que establezca wrapped a true, y un métodoisWrapped()que devuelva el estado de envoltura. Añade un métodogetStatus()que devuelva ya sea[item] (wrapped)o[item] (unwrapped)dependiendo del estado de envoltura.Registry.java: Crea una clase genérica con dos parámetros de tipo que empareje a un destinatario con su regalo. TuRegistry<K, V>debe tener campos privadosrecipientde tipoKygiftde tipoV. Incluye un constructor que acepte ambos valores, métodos gettergetRecipient()ygetGift(), y un métodogetEntry()que devuelva:[recipient] -> [gift]Main.java: ¡Dale vida a tu registro de regalos! Recibirás tres entradas: un nombre de destinatario (String), una descripción del regalo (String) y un valor del regalo (entero que representa dólares).Primero, crea un
GiftBox<String>que contenga la descripción del regalo. Envuélvelo usando el métodowrap(), luego imprime su estado.A continuación, crea un
GiftBox<Integer>que contenga el valor del regalo (que representa el monto de una tarjeta de regalo). No envuelvas este; imprime su estado para mostrar que está unwrapped.Finalmente, crea un
Registry<String, String>emparejando el nombre del destinatario con la descripción del regalo, e imprime la entrada del registro.
Recibirás tres entradas en orden: nombre del destinatario, descripción del regalo y valor del regalo (como un entero).
Observa cómo la misma clase GiftBox funciona perfectamente tanto con tipos String como Integer; ¡esa es la belleza de los genéricos! Y la clase Registry demuestra cómo los múltiples parámetros de tipo te permiten crear emparejamientos flexibles entre dos tipos cualesquiera.
Hoja de referencia
Una clase genérica declara uno o más parámetros de tipo entre corchetes angulares después del nombre de la clase, los cuales pueden utilizarse en toda la clase:
class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}La T es un parámetro de tipo: un marcador de posición que se reemplaza por un tipo real al crear una instancia. Las convenciones comunes utilizan letras mayúsculas individuales: T para Type (Tipo), E para Element (Elemento), K para Key (Clave), V para Value (Valor).
Al instanciar una clase genérica, especifique el tipo real entre corchetes angulares:
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // No casting needed
Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();El <> (operador diamante) permite que Java infiera el tipo a partir de la declaración.
Utilice múltiples parámetros de tipo para clases que trabajan con más de un tipo:
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> pair = new Pair<>("age", 25);Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Lee las entradas
String recipientName = scanner.nextLine();
String giftDescription = scanner.nextLine();
int giftValue = scanner.nextInt();
// TODO: Crea un GiftBox<String> que contenga la descripción del regalo (giftDescription)
// TODO: Envuélvelo usando el método wrap()
// TODO: Imprime su estado usando getStatus()
// TODO: Crea un GiftBox<Integer> que contenga el valor del regalo (giftValue)
// TODO: No envuelvas este - imprime su estado (debería mostrarse como no envuelto)
// TODO: Crea un Registry<String, String> emparejando al destinatario con la descripción del regalo
// TODO: Imprime la entrada del registro usando getEntry()
}
}
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 Iterator