Genéricos En Kotlin

ANUNCIO
Loading...

En este tutorial verás los fundamentos básicos para declarar y usar genéricos en Kotlin, con el fin de crear clases, interfaces y funciones que ejecuten código sin conocer el tipo del parámetro usado.

Parámetros Genéricos

Escribir clases, interfaces y funciones genéricas (generics) se basa en la introducción de parámetros en la sintaxis de sus declaraciones.

La lista de parámetros para tipos se incluyen en paréntesis angulares y se separan por coma si son varios.

<T, U, V,..>
Lenguaje del código: Kotlin (kotlin)

Esto permite que los tipos sean pasados como argumentos a la declaración del tipo genérico, el cual los usará en sus interior.

// Función de nivel superior genérica fun <T> funcionGenerica(param: T): T { return param } // Clase genérica class ClaseGenerica<T>(t: T) { val propiedad: T = t fun metodo(param: T) { println(param) } } // Interfaz genérica interface InterfazGenerica<T> { fun add(item: T) }
Lenguaje del código: Kotlin (kotlin)

Por ejemplo, cuando usas la interfaz List<E>, E es el parámetro de tipo formal y se podría leer como “una lista de E”. Lo que te permite crear listas parametrizadas según tu necesidad:

val numbers: List<Int> = listOf(1,2,3)
Lenguaje del código: Kotlin (kotlin)

Al igual que los tipos básicos, el tipo parametrizado se puede inferir por el compilador si el contexto o contenido de inicialización es explícita. La declaración anterior se puede escribir así:

val numbers = listOf(1,2,3)
Lenguaje del código: Kotlin (kotlin)

Declarar Funciones Genéricas

Para crear una función genérica debes añadir el tipo parámetro luego de la palabra fun.

El parámetro de tipo puede ser usado en:

  • La lista de parámetros regulares de la función
  • El valor de retorno
  • En el tipo recibidor de una función de extensión
  • En tipos función con recibidor

Por ejemplo:

fun <T> T.formatToPrint(): String = "Contenido: $this"
Lenguaje del código: Kotlin (kotlin)

La anterior función de extensión formatToPrint() es una función parametrizada por T como recibidor, donde cada objeto que sirva de argumento del parámetro, será usado para crear un String con el formato establecido.

fun main() { println("Ejemplo".formatToPrint()) println(6.formatToPrint()) println(6.4.formatToPrint()) println((6 > 5).formatToPrint()) }
Lenguaje del código: Kotlin (kotlin)

De esta forma es posible invocar la función sobre múltiples tipos, donde el resultado será el formato de contenido:

Contenido: Ejemplo Contenido: 6 Contenido: 6.4 Contenido: true
Lenguaje del código: HTTP (http)

Declarar Clases Genéricas

Declara clases e interfaces genéricas anteponiendo los paréntesis angulares con los parámetros de tipos antes del nombre de la clase. Con esto, los argumentos podrán usarse en el cuerpo del tipo genérico para declarar miembros.

class Generica<T>(/*constructor primario*/){ /*cuerpo*/}
Lenguaje del código: Kotlin (kotlin)

Por ejemplo:

Supongamos que deseamos diseñar el guardado de ítems de un jugador en un alijo con una capacidad máxima de compartimientos. Los ítems que pueden almacenarse están definidos por la siguiente interfaz:

interface StorableItem { val id: String val consumeSpace: Int }
Lenguaje del código: Kotlin (kotlin)

Los ítems almacenables se ven como las siguientes clases para cascos y botas:

data class Helmet( override val id: String, override val consumeSpace: Int = 1 ) : StorableItem data class Boots( override val id: String, override val consumeSpace: Int = 2 ) : StorableItem
Lenguaje del código: Kotlin (kotlin)

Para representar el alijo que contiene una lista de estos elementos usamos los paréntesis angulares con el parámetro de tipo formal T y especificamos una restricción para determinar que debe implementar a StorableItem.

class Stash<T : StorableItem>(var capacity: Int = 10) { private val itemsInStash = mutableListOf<T>() fun storeItem(item: T) { if (capacity - item.consumeSpace >= 0) { capacity -= item.consumeSpace itemsInStash.add(item) } } fun showItems() = itemsInStash .joinToString(separator = "\n") { it.id } }
Lenguaje del código: Kotlin (kotlin)

Con este simple diseño Stash te permite guardar ítems a través del método genérico storeItem() y es posible mostrar los ítems actuales del tipo parametrizado para itemsInStash con showItems().

fun main() { val helmetStash = Stash<Helmet>().apply { storeItem(Helmet("Casco de madera")) storeItem(Helmet("Casco de cuero")) } val bootsStash = Stash<Boots>().apply { storeItem(Boots("Botas maltrechas")) storeItem(Boots("Botas pesadas")) } println("Cascos:\n${helmetStash.showItems()}") println("Botas: \n${bootsStash.showItems()}") }
Lenguaje del código: Kotlin (kotlin)

Salida:

Cascos: Casco de madera Casco de cuero Botas: Botas maltrechas Botas pesadas

Como ves, se creó un alijo para cascos y otro para botas a través de la función apply{} y luego se imprimió el resultado a partir de Stash.showItems().

Incluso si deseas mezclar los ítems en un solo alijo, puedes declarar el tipo parametrizado Stash<StorableItem>. Así serán mezclados los elementos usando las características que StorableItem provee.

¿Ha sido útil esta publicación?