高阶函数和 lambda 表达式
Kotlin 函数是一等公民,这意味着它们可以存储在变量和数据结构中,并且可以作为参数传递给其他高阶函数或从其他高阶函数返回。您可以对函数执行任何适用于其他非函数值的操作。
为了实现这一点,Kotlin 作为一种静态类型编程语言,使用一系列函数类型来表示函数,并提供了一组专门的语言构造,例如 lambda 表达式。
fun main() {// 定义一个高阶函数。fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int {return operation(a, b)}// 使用 lambda 表达式作为参数。val sum = operateOnNumbers(3, 4) { x, y -> x + y }println("3 + 4 = $sum") // 3 + 4 = 7
}
高阶函数
高阶函数是接受函数作为参数或返回函数的函数。
高阶函数的一个很好的例子是函数式编程中的 fold
惯用法,用于集合。它接受一个初始累加器值和一个组合函数,并通过依次将当前累加器值与每个集合元素组合来构建其返回值,每次替换累加器值:
fun <T, R> Collection<T>.fold(initial: R,combine: (acc: R, nextElement: T) -> R
): R {var accumulator: R = initialfor (element: T in this) {accumulator = combine(accumulator, element)}return accumulator
}
使用:
fun main() {val list = listOf(2, 3, 4)/* * acc = 0, item = 2, acc + item* acc = 2, item = 3, acc + item* acc = 5, item = 4, acc + item*/list.fold(0) { acc, item ->println("acc = $acc, item = $item, acc + item");acc + item}/** acc = 1, item = 2, acc * item* acc = 2, item = 3, acc * item* acc = 6, item = 4, acc * item*/list.fold(1) { acc: Int, item: Int ->println("acc = $acc, item = $item, acc * item");acc * item}
}
函数类型
Kotlin 使用函数类型,例如 (Int) -> String
,来处理与函数相关的声明。
这些类型具有与函数签名(参数和返回值)对应的特殊表示法:
-
所有函数类型都有一个带括号的参数类型列表和一个返回类型
(A, B) -> C
表示一个类型,该类型表示接受类型为A
和B
的两个参数并返回类型为C
的值的函数。参数类型列表可以为空,例如() -> A
。Unit
返回类型不能省略。 -
函数类型可以选择性地具有一个额外的接收者类型,该类型在表示法中位于点之前:类型
A.(B) -> C
表示可以在接收者对象A
上调用的函数,该函数接受参数B
并返回值C
。 -
挂起函数属于一种特殊的函数类型,其表示法中带有
suspend
修饰符,例如suspend () -> Unit
或suspend A.(B) -> C
。
fun main() {val func1: () -> Unit = { println("click") }val func2: () -> Unit = fun () { println("press") }func1() // clickfunc2() // press
}
fun main() {val func1: (String) -> String = {name -> "Hello, $name!"}println(func1("Kotlin")) // Hello, Kotlin!val func2: String.(String) -> String = {other -> "Hello, $this and $other!"}println("Alice".func2("Bob")) // Hello, Alice and Bob!
}
函数类型表示法可以选择性地包含函数参数的名称:(x: Int, y: Int) -> Point
。这些名称可用于记录参数的含义。
要指定函数类型为可空,请使用括号:((Int, Int) -> Int)?
。
函数类型也可以使用括号组合:(Int) -> ((Int) -> Unit)
。
fun main() {val nullableFunction: ((Int, Int) -> Int)? = { x, y -> x + y }val combinedFunction: (Int) -> ((Int) -> Unit) = {x -> {y -> println(x + y)}}println("${nullableFunction?.invoke(3, 4)}") // 7combinedFunction(3)(4) // 7
}
箭头表示法是右结合的,(Int) -> (Int) -> Unit
等同于前面的示例,但与 ((Int) -> (Int)) -> Unit
不同。
fun main() {val func: (Boolean) -> (Int) -> Boolean = { bool -> {x ->println("x = $x, bool = $bool")bool}}// x = 0, bool = false// falseprintln(func(false)(0))val func2: (Boolean) -> ((Int) -> Boolean) = { bool -> {x ->println("x = $x, bool = $bool")bool}}// x = 1, bool = true// trueprintln(func2(true)(1))
}
fun main() {val func: (Boolean) -> (Int) -> Boolean = fun (bool: Boolean): (Int) -> Boolean {return fun (x: Int): Boolean {println("x = $x, bool = $bool")return bool}}// x = 1, bool = false// falseprintln(func(false)(1))
}
还可以使用类型别名为函数类型提供替代名称。
typealias ClickHandler = (Button, ClickEvent) -> Unit
实例化函数类型
有几种方法可以获取函数类型的实例:
-
使用函数字面量中的代码块,采用以下形式之一:
-
lambda 表达式:
{ a, b -> a + b }
-
匿名函数:
fun (s: String): Int { return s.toIntOrNull() ?: 0 }
-
-
带有接收者的函数字面量可以用作带有接收者的函数类型的值。
-
使用对现有声明的可调用引用:
-
顶层、局部、成员或扩展函数:
::isOdd
、String::toInt
-
顶层、成员或扩展属性:
List<Int>::size
-
构造函数:
::Regex
-
-
这些包括绑定指向特定实例成员的可调用引用:
foo::toString
。 -
使用实现函数类型作为接口的自定义类的实例。
fun main() {// 定义一个带有接收者的函数类型 StringBuilder.(String) -> Unit。val appendText: StringBuilder.(String) -> Unit = {// this 指向 StringBuilder 对象。this.append(it)}val sb = StringBuilder()sb.appendText("Hello, ")sb.appendText("Kotlin!")println(sb.toString()) // Hello, Kotlin!
}
fun main() {val appendText: StringBuilder.(String) -> Unit = { str -> this.append(str) }val sb = StringBuilder()sb.appendText("Hello, ")sb.appendText("Kotlin!")println(sb.toString()) // Hello, Kotlin!
}
// 顶层函数。
fun isOdd(n: Int): Boolean = n % 2 != 0fun main() {// 引用顶层函数。val func1: (Int) -> Boolean = ::isOddprintln(func1(3)) // trueprintln(func1(4)) // false// 局部函数。fun localFunc(n: Int): Boolean {return if (n == 1) true else false}// 引用局部函数。val func2: (Int) -> Boolean = ::localFuncprintln(func2(1)) // true
}
// 扩展函数。
fun String.myToInt(): Int {return this.toInt()
}fun main() {// 引用成员函数。val func1: String.() -> Int = String::toIntprintln("123".func1()) // 123// 引用扩展函数。val func2: String.() -> Int = String::myToIntprintln("123".func2()) // 123
}
// 顶层属性。
val pi: Int = 10// 扩展属性。
val String.isPalindrome: Booleanget() = this == this.reversed()fun main() {// 引用顶层属性。val myPi: () -> Int = ::pi// 引用成员属性。val myLength: (String) -> Int = String::length// 引用扩展属性。val myPalindrome: (String) -> Boolean = String::isPalindromeprintln(myPi()) // 10println(myLength("abc")) // 3println(myPalindrome("abc")) // false
}
fun main() {// 引用 String 的无参构造函数。val stringConstructor: () -> String = ::String// 调用构造函数。val emptyString = stringConstructor()println("Empty string: '$emptyString'") // Empty string: ''
}
fun main() {// 引用 String 的 CharArray 构造函数。val stringConstructor: (CharArray) -> String = ::String// 调用构造函数。val charArray = charArrayOf('H', 'e', 'l', 'l', 'o')val str = stringConstructor(charArray)println("String from char array: '$str'") // String from char array: 'Hello'
}
class Foo {override fun toString(): String = "Foo"
}fun main() {val foo = Foo()// 绑定可调用引用 foo::toString。val toStringGetter: () -> String = foo::toStringprintln(toStringGetter()) // Foo
}
class IntTransformer: (Int) -> Int {override operator fun invoke(x: Int): Int = TODO()
}val intFunction: (Int) -> Int = IntTransformer()
带有和不带有接收者的函数类型的非字面量值可以互换,因此接收者可以代替第一个参数,反之亦然。
例如,类型为 (A, B) -> C
的值可以在需要类型为 A.(B) -> C
的值的地方传递或赋值,反之亦然。
fun main() {// 普通函数类型。val add: (Int, Int) -> Int = { a, b -> a + b }// 带有接收者的函数类型。val addWithReceiver: Int.(Int) -> Int = { other -> this + other }// 互相赋值。val addAsReceiver: Int.(Int) -> Int = addval addAsNormal: (Int, Int) -> Int = addWithReceiverprintln("${add(3, 4)}") // 7println("${3.addWithReceiver(4)}") // 7
}
val repeatFun: String.(Int) -> String = { times -> this.repeat(times) }
val twoParameters: (String, Int) -> String = repeatFunfun runTransformation(f: (String, Int) -> String): String {return f("hello", 3)
}
val result = runTransformation(repeatFun)
默认情况下,推断的函数类型没有接收者,即使变量是用扩展函数的引用初始化的。要改变这一点,请显式指定变量类型。
fun main() {// 默认情况下,推断的函数类型没有接收者。val toInt: (String) -> Int = String::toInt// 显式指定变量类型。val toIntExplicit: String.() -> Int = String::toInt
}
调用函数类型实例
函数类型的值可以通过其 invoke(...)
操作符调用:f.invoke(x)
或直接 f(x)
。
如果该值具有接收者类型,则应将接收者对象作为第一个参数传递。
另一种调用带有接收者的函数类型值的方法是在其前面加上接收者对象,就像该值是扩展函数一样:1.foo(2)
。
val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plusprintln(stringPlus.invoke("<-", "->"))
println(stringPlus("Hello, ", "world!"))println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3))