Menu
Coddy logo textTech

Обобщённые функции

Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 37 из 57.

Обобщённая функция работает с любым типом. Её параметр типа, записанный в угловых скобках перед именем функции, обозначает тип, который использует вызывающий код. T — общепринятое имя:

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

Внутри main:

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

Вывод:

4
NONE

Kotlin выводит аргумент типа из аргументов. Если вывести его не из чего, вы указываете его самостоятельно, как в listOf<String>() выше:

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

Внутри main:

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

Результат:

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

У функции может быть несколько параметров типа, и параметр типа функции может их использовать. Так работают библиотечные функции, такие как map, для любого типа элемента и результата:

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 }

Внутри main:

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

Результат:

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

Параметр типа сохраняет тип вызывающего кода. firstOr(listOf(3, 5), 0) возвращает Int, поэтому + 1 компилируется. Версия, написанная для List<Any>, тоже принимала бы любой список, но возвращала бы Any и требовала бы приведения типа при каждом вызове:

fun firstAny(list: List<Any>, default: Any): Any = if (list.isEmpty()) default else list[0]
// firstAny(listOf(3, 5), 0) + 1               // ошибка: у Any нет plus
(firstAny(listOf(3, 5), 0) as Int) + 1         // компилируется, но нужен каст
firstOr(listOf(3, 5), 0) + 1                   // generic: результат — Int
challenge icon

Задание

Легко

Напишите две обобщённые функции, работающие со списками любого типа. countWhere(items, predicate) возвращает количество элементов, удовлетворяющих предикату — функции (T) -> Boolean. mostCommon(items) возвращает элемент, который встречается чаще всего (при одинаковом количестве возвращается тот, который встречается первым в списке), или null для пустого списка.

Предоставленный код считывает строку со словами и строку с числами, а затем выводит количество слов длиной более 3 букв, наиболее часто встречающееся слово, количество чётных чисел, наиболее часто встречающееся число и nothing: null для пустого списка.

Ваш код должен находиться в ListTools.kt. В Main.kt содержится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

fun main() {
    // Предоставленный код ввода/вывода: оставьте его как есть
    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 iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Kotlin