Menu
Coddy logo textTech

Funkcje generyczne

Część sekcji Programowanie obiektowe ścieżki Kotlin w Coddy. Lekcja 37 z 57.

Funkcja generyczna działa z dowolnym typem. Jej parametr typu, zapisany w nawiasach ostrych przed nazwą funkcji, oznacza typ używany przez wywołującego. T to zwyczajowa nazwa:

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

Wewnątrz main:

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

Wynik:

4
NONE

Kotlin wywnioskuje argument typu na podstawie argumentów. Gdy nie ma podstaw, by go wywnioskować, wpisujesz go samodzielnie, jak w przypadku listOf<String>() powyżej:

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

Wewnątrz main:

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

Wynik:

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

Funkcja może mieć kilka parametrów typów, a parametr typu funkcyjnego może ich używać. Tak działają funkcje biblioteczne, takie jak map, dla każdego typu elementu i wyniku:

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 }

Wewnątrz main:

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

Wynik:

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

Parametr typu zachowuje typ przekazany przez wywołującego. firstOr(listOf(3, 5), 0) zwraca Int, więc + 1 się kompiluje. Wersja napisana dla List<Any> również przyjęłaby każdą listę, ale zwracałaby Any i wymagałaby rzutowania przy każdym wywołaniu:

fun firstAny(list: List<Any>, default: Any): Any = if (list.isEmpty()) default else list[0]
// firstAny(listOf(3, 5), 0) + 1               // błąd: Any nie ma operatora plus
(firstAny(listOf(3, 5), 0) as Int) + 1         // kompiluje się, ale wymaga rzutowania
firstOr(listOf(3, 5), 0) + 1                   // generyczna: wynik jest typu Int
challenge icon

Wyzwanie

Łatwy

Napisz dwie funkcje generyczne, które działają z listami dowolnego typu. countWhere(items, predicate) zwraca liczbę elementów spełniających predykat, czyli funkcję (T) -> Boolean. mostCommon(items) zwraca element, który pojawia się najczęściej (ten, który pojawia się jako pierwszy na liście, gdy liczby wystąpień są równe), lub null dla pustej listy.

Dostarczony kod odczytuje wiersz słów i wiersz liczb, a następnie wyświetla liczbę słów dłuższych niż 3 litery, najczęściej występujące słowo, liczbę liczb parzystych, najczęściej występującą liczbę oraz nothing: null dla pustej listy.

Umieść swój kod w pliku ListTools.kt. Main.kt zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

fun main() {
    // Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
    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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Edytor online Kotlin