Introducción a los Generics en Kotlin
\n\nAl desarrollar en Kotlin, tarde o temprano te encontrarás con una situación en la que un mismo algoritmo o estructura de datos debe funcionar con diferentes tipos de objetos. Imagina que escribes una función para buscar el elemento máximo en una lista: debe manejar tanto una lista de números como una lista de strings. Sin herramientas especiales, tendrías que escribir implementaciones separadas para cada tipo. Aquí es donde los Generics (tipos genéricos o generalizaciones) vienen al rescate.
\n\nLos Generics en Kotlin son un mecanismo poderoso que permite crear clases, funciones e interfaces que trabajan con diferentes tipos de datos, manteniendo al mismo tiempo una tipificación estricta en tiempo de compilación. En este artículo, analizaremos para qué sirven los Generics y aprenderemos a usarlos con ejemplos prácticos.
\n\n¿Qué son los Generics y para qué sirven?
\n\nImagina una caja que puede guardar cualquier objeto: un libro, un teléfono o una manzana. En programación, esa "caja" es una clase o función genérica. Sin Generics, tendrías que usar el tipo Any (análogo a Object en Java) y hacer conversiones de tipo constantemente, lo que conlleva errores.
El problema sin Generics
\n\nfun <T> printList(list: List<T>) {\n for (item in list) {\n println(item)\n }\n}\n\n// Sin Generics tendrías que escribir así:\nfun printListAny(list: List<Any>) {\n for (item in list) {\n // Necesitas verificar el tipo y convertirlo\n if (item is String) {\n println("String: $item")\n } else if (item is Int) {\n println("Número: $item")\n }\n }\n}\n\nVentajas de usar Generics
\n\n- \n
- Seguridad de tipos — el compilador verifica la corrección del uso de tipos en tiempo de compilación. \n
- Reutilización de código — una función o clase funciona con cualquier tipo. \n
- Eliminación de conversiones explícitas — el código se vuelve más limpio y menos propenso a errores. \n
- Rendimiento — no hay sobrecarga por verificación de tipos en tiempo de ejecución. \n
Construcciones principales de Generics en Kotlin
\n\n1. Funciones Genéricas
\n\nUna función genérica es una función que acepta un parámetro de tipo. Se declara usando corchetes angulares <T> antes del nombre de la función.
fun <T> getFirstElement(list: List<T>): T? {\n return list.firstOrNull()\n}\n\nfun main() {\n val numbers = listOf(1, 2, 3, 4, 5)\n val firstNumber = getFirstElement(numbers)\n println("Primer número: $firstNumber") // 1\n\n val words = listOf("Gato", "Perro", "Loro")\n val firstWord = getFirstElement(words)\n println("Primera palabra: $firstWord") // Gato\n}\n\nObserva: el tipo T se deduce automáticamente por el compilador. Puedes especificarlo explícitamente, pero no es necesario:
val firstNumber = getFirstElement<Int>(numbers)\n\n2. Clases Genéricas
\n\nUna clase genérica es una clase que puede almacenar y procesar objetos de cualquier tipo. Un excelente ejemplo es la clase estándar Box.
class Box<T>(private val content: T) {\n fun getContent(): T {\n return content\n }\n\n fun isTypeOf(typeClass: Class<*>): Boolean {\n return typeClass.isInstance(content)\n }\n}\n\nfun main() {\n val stringBox = Box("¡Hola, mundo!")\n println(stringBox.getContent()) // ¡Hola, mundo!\n\n val intBox = Box(42)\n println(intBox.getContent()) // 42\n\n // Verificación de tipo\n println(intBox.isTypeOf(Int::class.java)) // true\n}\n\n3. Interfaces Genéricas
\n\nLas interfaces también pueden ser genéricas. Esto es especialmente útil al crear colecciones o contratos para el procesamiento de datos.
\n\ninterface Repository<T> {\n