ProgramaciónDesarrollador Backend

¿Cuáles son los aspectos clave del trabajo con la generación y uso de genéricos en Java, qué sutilezas es importante conocer para un uso seguro?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Los genéricos permiten crear clases, interfaces y métodos con parámetros de tipo, lo que asegura la verificación de tipo en tiempo de compilación y ayuda a evitar ClassCastException.

Características clave y trampas:

  • Borrado de tipos: En tiempo de compilación, la información sobre los tipos de los parámetros se elimina, por lo que no se puede, por ejemplo, crear un arreglo de tipo genérico: new List<String>[10] — error de compilación.
  • Restricciones de uso:
    • No se pueden crear instancias de un tipo parametrizado: T obj = new T();
    • No se puede usar instanceof con tipos parametrizados: if(obj instanceof List<String>) — error.
    • No se pueden crear campos estáticos de tipos parametrizados.
  • Códigos comodines: ? extends T — covarianza (lectura), ? super T — contravarianza (escritura).
  • Principio PECS (Producer Extends, Consumer Super): Si solo necesita leer, utilice extends; si necesita escribir, utilice super.

Ejemplo:

// Enfoque covariante para lectura void printNumbers(List<? extends Number> numbers) { for (Number n : numbers) { System.out.println(n); } } // Enfoque contravariante para escritura void addIntegers(List<? super Integer> list) { list.add(10); } }

Pregunta capciosa.

Pregunta: "¿Cuál es la diferencia entre List<Object> y List<?>? ¿Se puede poner cualquier objeto en List<?>?"

Respuesta: No, en List<?> no se puede agregar nada (excepto null), porque el compilador no sabe qué tipo de parámetro hay. En List<Object> se pueden agregar cualquier objeto.

Ejemplo:

List<?> list1 = new ArrayList<String>(); // list1.add("test"); // ¡Error de compilación! List<Object> list2 = new ArrayList<>(); list2.add("test"); // OK

Ejemplos de errores reales por desconocimiento de las sutilezas del tema.


Historia

Un equipo de desarrolladores intentó implementar una caché basada en un arreglo de tipo parametrizado T[]. Debido al borrado de tipos y la imposibilidad de crear arreglos de tipos genéricos, la solución no funcionó como se esperaba: se obtenía un arreglo Object[], que llevaba a ClassCastException en los castings en tiempo de ejecución.


Historia

En uno de los microservicios, un desarrollador intentó implementar un receptor que utilizaba List<?> como parámetro, y trató de modificar la colección. Esto provocó un error de compilación y retrasó el lanzamiento, ya que tuvo que refactorizar la lógica teniendo en cuenta PECS.


Historia

En un proyecto de integración con un sistema externo, un desarrollador cometió un error al sobrescribir una colección de un tipo con otra a través de un raw-type no manejado: List list = new ArrayList<String>(), lo que condujo a ClassCastException y caídas del servicio en producción al intentar castear elementos a otros tipos.