ProgramaciónDesarrollador Backend

¿Cómo se implementan los generics en Kotlin? ¿Cuáles son las restricciones, cómo funcionan la invariancia, la covarianza y la contravarianza, y en qué se diferencian de los generics en Java? Proporcione ejemplos de uso.

Supere entrevistas con el asistente de IA Hintsage

Respuesta

Generics en Kotlin permiten crear estructuras de datos y funciones universales y seguras en cuanto a tipos. La característica principal: Kotlin implementa un sistema de tipos genéricos a nivel de compilación, igual que Java, pero con una tipificación más estricta y una sintaxis de varianza (covarianza y contravarianza) más extensa.

Restricciones de los generics:

  • Los parámetros de tipo son invariantes por defecto.
  • No se puede crear una instancia del parámetro de tipo (T() está prohibido).
  • No hay acceso a la información de tipos genéricos en tiempo de ejecución (borrado de tipos).

Varianza:

  • Covarianza (out T): permite usar subtipos.
  • Contravarianza (in T): permite usar supertipos.
  • Invarianza: tipo sin modificador de varianza.

Ejemplo de covarianza:

interface Producer<out T> { fun produce(): T }

Ejemplo de contravarianza:

interface Consumer<in T> { fun consume(item: T) }

Diferencia con Java:

  • La sintaxis es más explícita y concisa (out en lugar de ? extends, in en lugar de ? super).
  • No hay tipos comodín (?, solo in/out).
  • Prohibición de crear instancias del parámetro genérico.

Pregunta capciosa

Pregunta: "¿Se puede declarar un array de arrays (Array<Array<Int>>) como Array<out Array<Int>> en Kotlin y qué ocurrirá al intentar escribir en tal array?"

Respuesta: Sí, se puede declarar como Array<out Array<Int>>, pero tal array se convierte en solo lectura (read-only):

val arr: Array<out Array<Int>> = Array(1) { Array(1) { 0 } } arr[0] = arrayOf(1, 2, 3) // ¡Error de compilación!

Intentar escribir un valor resultará en un error: el array genérico con un parámetro out no permite escribir elementos, porque de lo contrario se violaría la seguridad de tipos.

Ejemplos de errores reales debido a la falta de conocimiento de los matices del tema


Historia

En el equipo intentaron crear un array de objetos genéricos con un tipo de parámetro out, y luego poner valores en él a través de set(index, value). El código se compiló, pero lanzó un error en tiempo de ejecución y varias funciones dejaron de funcionar.


Historia

Una vez, al migrar una biblioteca de Java a Kotlin, dejaron tipos comodín (? extends ...), y en Kotlin simplemente copiaron los tipos sin cambiar a out/in. El resultado: la compilación falló, y al "recorrer" el código, el error ocurrió en tiempo de ejecución, complicando el proceso de depuración.


Historia

Usaron varianza in/out con una clase personalizada, pero confundieron los modificadores, declarando interface Stack<in T> en lugar de Stack<out T>. Esto resultó en la imposibilidad de devolver elementos de la pila: la firma del método violaba el contrato de sistema out/in.