Menu
Coddy logo textTech

Funciones genéricas

Parte de la sección Programación orientada a objetos del Journey de Kotlin de Coddy. Lección 37 de 57.

Una función genérica funciona con cualquier tipo. Su parámetro de tipo, escrito entre corchetes angulares antes del nombre de la función, representa el tipo que utiliza quien llama a la función. T es el nombre habitual:

fun <T> firstOr(list: List<T>, default: T): T = if (list.isEmpty()) default else list[0]

Dentro de main:

println(firstOr(listOf(3, 5), 0) + 1)
println(firstOr(listOf<String>(), "none").uppercase())

Salida:

4
NONE

Kotlin infiere el argumento de tipo a partir de los argumentos. Cuando no hay nada de lo que inferirlo, lo escribes tú mismo, como en listOf<String>() arriba:

fun <T> repeatItem(item: T, times: Int): List<T> = List(times) { item }

Dentro de main:

println(repeatItem("ab", 3))
println(repeatItem(7, 2))
val empty = repeatItem<Double>(1.5, 0)
println(empty)

Salida:

[ab, ab, ab]
[7, 7]
[]

Una función puede tener varios parámetros de tipo, y un parámetro de tipo función puede utilizarlos. Así es como funcionan las funciones de biblioteca como map para cualquier tipo de elemento y de resultado:

fun <A, B> transform(items: List<A>, f: (A) -> B): List<B> {
    val out = mutableListOf<B>()
    for (item in items) out.add(f(item))
    return out
}

fun <K, V> invert(map: Map<K, V>): Map<V, K> = map.entries.associate { (k, v) -> v to k }

Dentro de main:

println(transform(listOf("kiwi", "fig")) { it.length })
println(invert(mapOf("a" to 1, "b" to 2)))

Salida:

[4, 3]
{1=a, 2=b}

Un parámetro de tipo conserva el tipo del llamador. firstOr(listOf(3, 5), 0) devuelve un Int, por lo que + 1 se compila. Una versión escrita para List<Any> también aceptaría cualquier lista, pero devolvería Any y obligaría a hacer una conversión de tipo en cada llamada:

fun firstAny(list: List<Any>, default: Any): Any = if (list.isEmpty()) default else list[0]
// firstAny(listOf(3, 5), 0) + 1               // error: Any no tiene plus
(firstAny(listOf(3, 5), 0) as Int) + 1         // compila, pero necesita un cast
firstOr(listOf(3, 5), 0) + 1                   // genérico: el resultado es un Int
challenge icon

Desafío

Fácil

Escribe dos funciones genéricas que funcionen con listas de cualquier tipo. countWhere(items, predicate) devuelve cuántos elementos cumplen el predicado, una función (T) -> Boolean. mostCommon(items) devuelve el elemento que aparece con mayor frecuencia (el que aparece primero en la lista cuando hay empate en los recuentos), o null para una lista vacía.

El código proporcionado lee una línea de palabras y una línea de números, e imprime la cantidad de palabras con más de 3 letras, la palabra más común, la cantidad de números pares, el número más común y nothing: null para una lista vacía.

Tu código va en ListTools.kt. Main.kt contiene el código proporcionado de entrada/salida y no se puede editar.

Pruébalo tú mismo

fun main() {
    // Código de entrada/salida suministrado: déjalo como está
    val input = generateSequence(::readLine).toList()
    val words = input[0].split(" ")
    val numbers = input[1].split(" ").map { it.toInt() }
    println("long words: ${countWhere(words) { it.length > 3 }}")
    println("most common word: ${mostCommon(words)}")
    println("even numbers: ${countWhere(numbers) { it % 2 == 0 }}")
    println("most common number: ${mostCommon(numbers)}")
    println("nothing: ${mostCommon(listOf<String>())}")
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Playground de Kotlin