GENERICOS EN JAVA
Conceptos Básicos de Genéricos
1. Definición de Genéricos: Los genéricos permiten que las clases, interfaces y
métodos operen sobre tipos especificados por el usuario, proporcionando una
forma de reutilizar el código y mejorar la seguridad de tipos en tiempo de
compilación.
2. Sintaxis Básica: La sintaxis básica de los genéricos utiliza ángulos < > para
definir un parámetro de tipo.
class Caja<T> {
private T contenido;
public void setContenido(T contenido) {
[Link] = contenido;
}
public T getContenido() {
return contenido;
}
}
3. Parámetros de Tipo:
o T: Tipo (Type)
o E: Elemento (Element, usado en colecciones)
o K: Clave (Key)
o V: Valor (Value)
o N: Número (Number)
Ejemplos Prácticos
1. Clase Genérica Simple
public class Caja<T> {
private T contenido;
public void setContenido(T contenido) {
[Link] = contenido;
}
public T getContenido() {
return contenido;
}
public static void main(String[] args) {
Caja<String> cajaDeTexto = new Caja<>();
[Link]("Hola, Genéricos!");
[Link]([Link]());
Caja<Integer> cajaDeNumeros = new Caja<>();
[Link](123);
[Link]([Link]());
}
}
2. Métodos Genéricos
public class Util {
public static <T> void imprimir(T elemento) {
[Link](elemento);
}
public static void main(String[] args) {
[Link]("Hola, Mundo!");
[Link](123);
[Link](45.67);
}
}
4. Interfaces Genéricas
CREAR UNA INTERFAZ EN EL MISMO PAQUEE DONDE RESIDE EL
MAIN
interface Contenedor<T> {
void agregar(T elemento);
T obtener();
}
CREAR UNA CLASE QUE IMPEMENTE LA INTERFAZ
class ContenedorSimple<T> implements Contenedor<T> {
private T elemento;
@Override
public void agregar(T elemento) {
[Link] = elemento;
}
@Override
public T obtener() {
return elemento;
}
PROBAR EL METODO MAIN
public static void main(String[] args) {
Contenedor<String> contenedor = new ContenedorSimple<>();
[Link]("Elemento Genérico");
[Link]([Link]());
}
}
Bounded Type Parameters (Parámetros de Tipo Acotados)
Puedes restringir los tipos que se pueden usar como parámetros de tipo.
CREAR UNA CLASE QUE SOLO HEREDE DE NUMBER EN EL
MISMO PAQUETE DONDE RESIDE EL MAIN
public class CajaNumerica<T extends Number> {
private T numero;
public void setNumero(T numero) {
[Link] = numero;
}
public T getNumero() {
return numero;
}
PROBARLA EN EL MAIN
public static void main(String[] args) {
CajaNumerica<Integer> cajaDeEnteros = new CajaNumerica<>();
[Link](10);
[Link]([Link]());
CajaNumerica<Double> cajaDeDoubles = new CajaNumerica<>();
[Link](10.5);
[Link]([Link]());
}
}
Wildcards (Comodines)
Los comodines (?) se utilizan cuando deseas especificar un parámetro de tipo
desconocido.
Upper Bounded Wildcards
CREAR UNA CLASE EN EL MISMO PAQUETE DONDE RESIDE EL
MAIN
public static void imprimirLista(List<? extends Number> lista) {
for (Number numero : lista) {
[Link](numero);
}
}
public static void main(String[] args) {
List<Integer> listaDeEnteros = [Link](1, 2, 3);
List<Double> listaDeDoubles = [Link](1.1, 2.2, 3.3);
imprimirLista(listaDeEnteros);
imprimirLista(listaDeDoubles);
}
Lower Bounded Wildcards
public static void agregarNumeroALista(List<? super Integer> lista) {
[Link](10);
}
public static void main(String[] args) {
List<Number> listaDeNumeros = new ArrayList<>();
agregarNumeroALista(listaDeNumeros);
[Link](listaDeNumeros);
}
Erasure (Eliminación de Tipos)
Java implementa los genéricos usando "type erasure" (eliminación de tipos), lo que
significa que la información de tipos genéricos es borrada en tiempo de ejecución, y las
verificaciones de tipo se realizan en tiempo de compilación.
Los Lower Bounded Wildcards (comodines acotados por abajo) en Java son una
característica de los genéricos que te permite definir un tipo genérico que puede ser un
tipo específico o cualquier superclase de ese tipo específico. Se utiliza la sintaxis ?
super T, donde T es el tipo específico.
Conceptos Clave
● Sintaxis: List<? super T>
● Propósito: Permitir que el tipo sea T o cualquier superclase de T.
● Uso Común: Es útil cuando se necesita escribir métodos que pueden aceptar
argumentos de múltiples tipos relacionados por herencia, pero se debe garantizar
que los tipos pasen al menos una cierta clase base.
Los "Lower Bounded Wildcards" (comodines acotados por abajo) en Java se utilizan
cuando deseas trabajar con un tipo genérico que es un supertipo (tipo padre) de un tipo
específico. Esto te permite restringir un método para aceptar tipos que son supertypos
de un tipo dado, brindando mayor flexibilidad.
Sintaxis
La sintaxis para un lower bounded wildcard es <? super T>, donde T es el tipo
específico y el wildcard permitirá cualquier supertipo de T.
Ejemplo de Uso
Supongamos que tienes una lista y quieres agregar elementos a esta lista. Quieres
asegurarte de que la lista pueda aceptar el tipo específico que deseas agregar o cualquier
supertipo de ese tipo. Aquí hay un ejemplo:
CREAR UNA CLASE EN EL MISMO PAQUETE DONDE RESIDE EL
MAIN
import [Link];
import [Link];
public class LowerBoundedWildcardExample {
public static void agregarNumeroALista(List<? super Integer> lista)
{
[Link](10);
[Link](20);
}
public static void main(String[] args) {
List<Number> listaDeNumeros = new ArrayList<>();
agregarNumeroALista(listaDeNumeros);
[Link](listaDeNumeros);
List<Object> listaDeObjetos = new ArrayList<>();
agregarNumeroALista(listaDeObjetos);
[Link](listaDeObjetos);
}
}
Explicación
1. Definición del Método:
public static void agregarNumeroALista(List<? super Integer>
lista)
Aquí, List<? super Integer> significa que el método agregarNumeroALista
puede aceptar una lista de cualquier tipo que sea un supertipo de Integer. Esto
incluye Integer, Number, y Object.
2. Agregar Elementos a la Lista:
[Link](10);
[Link](20);
Se agregan elementos de tipo Integer a la lista. Dado que la lista puede aceptar
Integer o cualquier supertipo de Integer, esto es válido.
3. Uso del Método:
List<Number> listaDeNumeros = new ArrayList<>();
agregarNumeroALista(listaDeNumeros);
[Link](listaDeNumeros);
Una lista de Number se pasa al método, y es válida porque Number es un
supertipo de Integer.
List<Object> listaDeObjetos = new ArrayList<>();
agregarNumeroALista(listaDeObjetos);
[Link](listaDeObjetos);
También se puede pasar una lista de Object, porque Object es un supertipo de
Integer.
Beneficios de los Lower Bounded Wildcards
● Flexibilidad: Permiten que un método trabaje con una jerarquía de tipos en
lugar de un solo tipo específico.
● Seguridad de Tipos: Ayudan a asegurar que los tipos de datos sean manejados
correctamente, previniendo errores de tipo en tiempo de compilación.
Casos de Uso Comunes
● Métodos de Adición: Cuando necesitas agregar elementos a una colección y
quieres aceptar colecciones que pueden contener el tipo específico o cualquier
supertipo de ese tipo.
● **Procesamiento
Recursos Adicionales
Para profundizar en los genéricos en Java, aquí tienes algunos recursos recomendados:
● Libros:
o "Effective Java" de Joshua Bloch (Capítulo sobre Genéricos)
o "Java Generics and Collections" de Maurice Naftalin y Philip Wadler
● Documentación Oficial:
o Tutorial de Genéricos en Java
o Java SE Documentation - Generics
● Artículos y Blogs:
o Guía de Genéricos en Java
o Java Generics Tutorial
Como crear una clase Genérica que simule las operaciones de
un CRUD en memoria
Veamos la siguiente clase Genérica llamada InMemoryRepository<T> en el mismo
paquete donde reside el main:
public class InMemoryRepository<T> {
protected Map<Long, T> data = new HashMap<>();
protected AtomicLong idGenerator = new AtomicLong();
public T save(T entity) {
long id = [Link]();
// Suponiendo que las entidades tienen un método setId
try {
String clase;
[Link]().getMethod("setId",
[Link]).invoke(entity, id);
clase = [Link]().getName();
[Link](clase + " id :" + id);
} catch (Exception e) {
[Link]();
}
[Link](id, entity);
return entity;
}
public Optional<T> findById(Long id) {
return [Link]([Link](id));
}
public List<T> findAll() {
return new ArrayList<>([Link]());
}
public Optional<T> genericUpdate(Long id, T updatedEntity) {
if () {
return [Link]();
}
try {
// Establecer el mismo ID en la entidad actualizada para
mantener la coherencia
Method setIdMethod =
[Link]().getMethod("setId", [Link]);
[Link](updatedEntity, id);
[Link](id, updatedEntity);
return [Link](updatedEntity);
} catch (Exception e) {
[Link]();
return [Link]();
}
}
public Optional<T> genericDelete(Long id) {
if () {
return [Link]();
}
return [Link]([Link](id));
}
public List<T> genericFindByField(String fieldName, Object value) {
List<T> results = new ArrayList<>();
try {
for (T entity : [Link]()) {
Method getFieldMethod =
[Link]().getMethod("get" + capitalize(fieldName));
Object fieldValue = [Link](entity);
if (fieldValue != null && [Link](value)) {
[Link](entity);
}
}
} catch (Exception e) {
[Link]();
}
return results;
}
private String capitalize(String str) {
if (str == null || [Link]()) {
return str;
}
return [Link](0, 1).toUpperCase() + [Link](1);
}
}
EXPLICACION
La clase InMemoryRepository<T> es un repositorio genérico en memoria que
permite gestionar entidades de cualquier tipo T. Esta clase proporciona métodos
para guardar, buscar, actualizar y eliminar entidades, así como para encontrar
entidades por campos específicos. Aquí está una explicación detallada de cada
componente y método de la clase:
Componentes de la Clase
1. protected Map<Long, T> data = new HashMap<>();
o Un mapa que almacena las entidades en memoria. La clave es un Long
que representa el ID de la entidad y el valor es la propia entidad.
2. protected AtomicLong idGenerator = new AtomicLong();
o Un generador de IDs que se incrementa automáticamente cada vez que se
guarda una nueva entidad, asegurando que cada entidad tenga un ID
único.
Métodos de la Clase
1. public T save(T entity)
o Guarda una entidad en el repositorio.
o Genera un nuevo ID para la entidad utilizando idGenerator.
o Utiliza reflexión para invocar el método setId de la entidad y asignarle
el ID generado.
o Almacena la entidad en el mapa data.
o Retorna la entidad guardada.
public T save(T entity) {
long id = [Link]();
try {
[Link]().getMethod("setId",
[Link]).invoke(entity, id);
[Link]([Link]().getName() + " id :"
+ id);
} catch (Exception e) {
[Link]();
}
[Link](id, entity);
return entity;
}
2. public Optional<T> findById(Long id)
o Busca una entidad por su ID.
o Retorna un Optional que puede contener la entidad si se encuentra, o
estar vacío si no se encuentra.
public Optional<T> findById(Long id) {
return [Link]([Link](id));
}
3. public List<T> findAll()
o Retorna una lista con todas las entidades almacenadas en el repositorio.
public List<T> findAll() {
return new ArrayList<>([Link]());
}
4. public Optional<T> genericUpdate(Long id, T updatedEntity)
o Actualiza una entidad existente en el repositorio.
o Comprueba si el ID existe en el mapa data.
o Si existe, utiliza reflexión para establecer el mismo ID en la entidad
actualizada.
o Almacena la entidad actualizada en el mapa y retorna un Optional que
contiene la entidad actualizada.
o Si no existe, retorna un [Link]().
public Optional<T> genericUpdate(Long id, T updatedEntity) {
if () {
return [Link]();
}
try {
Method setIdMethod =
[Link]().getMethod("setId", [Link]);
[Link](updatedEntity, id);
[Link](id, updatedEntity);
return [Link](updatedEntity);
} catch (Exception e) {
[Link]();
return [Link]();
}
}
5. public Optional<T> genericDelete(Long id)
o Elimina una entidad del repositorio por su ID.
o Retorna un Optional que puede contener la entidad eliminada si se
encuentra, o estar vacío si no se encuentra.
public Optional<T> genericDelete(Long id) {
if () {
return [Link]();
}
return [Link]([Link](id));
}
6. public List<T> genericFindByField(String fieldName, Object
value)
o Busca entidades en el repositorio por un campo específico y su valor.
o Utiliza reflexión para obtener el valor del campo especificado en cada
entidad.
o Retorna una lista de entidades que tienen el valor especificado en el
campo.
public List<T> genericFindByField(String fieldName, Object value)
{
List<T> results = new ArrayList<>();
try {
for (T entity : [Link]()) {
Method getFieldMethod =
[Link]().getMethod("get" + capitalize(fieldName));
Object fieldValue = [Link](entity);
if (fieldValue != null && [Link](value)) {
[Link](entity);
}
}
} catch (Exception e) {
[Link]();
}
return results;
}
7. private String capitalize(String str)
o Capitaliza la primera letra de una cadena. Este método es útil para
construir nombres de métodos getter basados en nombres de campos.
private String capitalize(String str) {
if (str == null || [Link]()) {
return str;
}
return [Link](0, 1).toUpperCase() + [Link](1);
}
Ejemplo de Uso del InMemoryRepository aplicado a la clase persona
La clase InMemoryRepository permite gestionar entidades en memoria de manera
genérica. Por ejemplo, puedes crear un repositorio para una clase Persona de la
siguiente manera:
CREAR LA CLASE PERSONA EN EL MISMO PAQUETE DONDE
RESIDE EL MAIN
public class Persona {
private Long id;
private String nombre;
private String apellido;
private Integer edad;
// Getters y setters
public Long getId() {
return id;
}
public void setId(Long id) {
[Link] = id;
}
// Otros getters y setters, así como métodos adicionales
}
EN EL MAIN
public class Main {
public static void main(String[] args) {
InMemoryRepository<Persona> repo = new InMemoryRepository<>();
Persona persona = new Persona();
[Link]("Juan");
[Link]("Perez");
[Link](30);
// Guardar persona
[Link](persona);
// Encontrar por ID
Optional<Persona> encontrada = [Link](1L);
[Link](p -> [Link]([Link]()));
// Encontrar todas
List<Persona> todas = [Link]();
[Link](p -> [Link]([Link]()));
}
}
Este código muestra cómo crear un repositorio para la clase Persona, guardar una
instancia, buscarla por ID y obtener todas las instancias almacenadas.
Ejercitación Propuesta:
Tomando la estructura anterior crear 5 personas distintas y probar toda
la funcionalidad de los métodos de la clase InMemoryRepository.
Cuestionario sobre Genéricos en Java
1. ¿Qué son los genéricos en Java?
o A) Una forma de escribir código independiente del tipo.
o B) Una forma de evitar errores de compilación.
o C) Una manera de mejorar la legibilidad del código.
o D) Un tipo de colección en Java.
o Respuesta Correcta: A
2. ¿Qué permite el uso de genéricos en Java?
o A) El uso de operadores sobrecargados.
o B) La reutilización del código con diferentes tipos de datos.
o C) La creación de nuevos tipos de datos.
o D) La optimización del rendimiento.
o Respuesta Correcta: B
3. ¿Cuál es la sintaxis correcta para declarar una clase genérica en Java?
o A) class MyClass<T> { }
o B) class MyClass[T] { }
o C) class MyClass { <T> }
o D) class MyClass { T }
o Respuesta Correcta: A
4. ¿Qué significa <T extends Number> en una declaración de genéricos?
o A) T puede ser cualquier tipo.
o B) T debe ser una subclase de Number.
o C) T debe ser una superclase de Number.
o D) T debe ser exactamente Number.
o Respuesta Correcta: B
5. ¿Cuál es el propósito de los "wildcards" (comodines) en genéricos?
o A) Para permitir cualquier tipo en un método.
o B) Para restringir el tipo de datos a un conjunto específico.
o C) Para facilitar el uso de herencia con tipos genéricos.
o D) Para optimizar el rendimiento del código.
o Respuesta Correcta: C
6. ¿Qué representa List<?> en una declaración de método?
o A) Una lista de cualquier tipo específico.
o B) Una lista que no acepta elementos.
o C) Una lista de tipos desconocidos.
o D) Una lista vacía.
o Respuesta Correcta: C
7. ¿Qué son los "upper bounded wildcards" en genéricos?
o A) Wildcards que permiten cualquier tipo.
o B) Wildcards que permiten supertypos.
o C) Wildcards que permiten subtipos.
o D) Wildcards que restringen el tipo a ser exactamente uno.
o Respuesta Correcta: C
8. ¿Cómo se declara un método que acepta cualquier supertipo de Integer?
o A) public void metodo(List<? super Integer> list)
o B) public void metodo(List<Integer> list)
o C) public void metodo(List<? extends Integer> list)
o D) public void metodo(List<?> list)
o Respuesta Correcta: A
9. ¿Cuál de las siguientes declaraciones es correcta para crear una instancia
de una clase genérica?
o A) MyClass<int> obj = new MyClass<>();
o B) MyClass<T> obj = new MyClass<T>();
o C) MyClass<String> obj = new MyClass<>();
o D) MyClass<?> obj = new MyClass<?>()
o Respuesta Correcta: C
10.¿Qué beneficio proporciona el uso de genéricos en colecciones como
ArrayList?
o A) Mayor rendimiento.
o B) Seguridad de tipos en tiempo de compilación.
o C) Menor uso de memoria.
o D) Capacidad de almacenar solo tipos primitivos.
o Respuesta Correcta: B
Este cuestionario cubre una variedad de conceptos clave relacionados con los genéricos
en Java, incluyendo su sintaxis, uso, y los beneficios que proporcionan.