Menu
CoddyTech

泛型函数

本课属于 Coddy Kotlin 学习旅程的“面向对象编程”单元。第 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                   // 泛型:结果是 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 在线编辑器