Generics en Kotlin: para qué sirven y cómo usarlos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción a los Generics en Kotlin



Los Generics son una de las características clave del lenguaje Kotlin que permite escribir código flexible, type-safe y reutilizable. Si alguna vez has escrito List<String> o Map<Int, User>, ya has usado genéricos, incluso sin pensar en ello. Pero, ¿qué hay detrás de esos corchetes angulares y cómo sacarles el máximo provecho? En este artículo analizaremos para qué sirven los Generics en Kotlin, cómo aplicarlos en la práctica y qué características avanzadas (variance, reified, where) te ayudarán a escribir código aún más limpio y seguro.



Este artículo está dirigido a desarrolladores que ya conocen los fundamentos de Kotlin y quieren profundizar sus conocimientos. Cubriremos todo: desde la sintaxis básica hasta ejemplos reales de código en producción.



¿Para qué sirven los Generics? El problema sin ellos



Imagina que necesitas escribir una función que reciba una lista de cualquier elemento y devuelva el primer elemento. Sin genéricos, escribirías así:



fun getFirst(list: List<Any>): Any {    return list[0]}


Ahora, si pasas List<String>, obtendrás Any como resultado. Para trabajar con él como si fuera un string, tendrías que hacer una conversión explícita de tipos:



val first = getFirst(listOf("Kotlin", "Java")) as Stringfirst.length // ¡Peligroso! Puede lanzar ClassCastException


Esto es incómodo e inseguro. El error solo se manifestará en tiempo de ejecución. Los Generics resuelven este problema conservando la información del tipo en tiempo de compilación.



Principales ventajas de los Generics:

  • Type-safety — el compilador verifica los tipos en tiempo de compilación, no en tiempo de ejecución.
  • Eliminación de conversiones explícitas — el código se vuelve más limpio y legible.
  • Reutilización — una clase o función funciona con diferentes tipos.


Sintaxis básica: clases, funciones y restricciones



Clases genéricas



La forma más sencilla de empezar es declarar una clase con uno o varios parámetros de tipo. Los parámetros de tipo se indican entre corchetes angulares <T> (la letra puede ser cualquiera, pero tradicionalmente se usan T, E, K, V):



class Box<T>(val value: T)

fun main() { val stringBox = Box("Kotlin") // El tipo se infiere automáticamente val intBox = Box(42) // Box<Int>

println(stringBox.value.length) // ¡Funciona sin conversión! println(intBox.value + 1) // 43}


Kotlin puede inferir el tipo, por lo que no es necesario escribir Box<String> — basta con Box("Kotlin").



Funciones genéricas



Las funciones también pueden ser genéricas. El parámetro de tipo se indica antes del nombre de la función:



fun <T> getMiddleElement(list: List<T>): T? {    if (list.isEmpty()) return null    return list[list.size / 2]}

fun main() { val numbers = listOf(1, 2, 3, 4, 5) val middle = getMiddleElement(numbers) // El tipo Int? se infiere automáticamente println(middle) // 3}


Restricciones (where / upper bound)



A veces es necesario limitar qué tipos se pueden usar en un genérico. Por ejemplo, para llamar al método .compareTo(), el tipo debe implementar la interfaz Comparable:



fun <T : Comparable<T>> maxOfTwo(a: T, b: T): T {    return if (a >= b) a else b}

fun main() { println(maxOfTwo(10, 20)) // 20 println(maxOfTwo("A", "B")) // B // maxOfTwo(10, "A") // Error de compilación: los tipos no coinciden}


Si se necesitan varias restricciones, usa la palabra clave where:



interface Printable {    fun print()}

fun <T> process(item: T) where T : Comparable<T>, T : Printable

Blogs

Book Recommendations