本篇内容清单如下:
- 高阶函数
- 函数类型
- Lambda 表达式
- 匿名函数
- 闭包
- 内联函数
Kotlin 函数使用场景:
- 存储在变量 与 数据结构中;
- 作为 参数 传递给其他 高阶函数;
- 从其他高阶函数返回。
也即是可以像操作 变量 一样操作 函数。
作为一门静态语言,Kotlin 使用一系列 函数类型 来表示 函数,并提供一组特定的语言结构,如 lambda 表达式。
1. 高阶函数
- 定义:将函数用作 参数 或 返回值 的函数。
2. 函数类型
- 类似
(Int) -> String
的一系列 函数类型 来处理函数的声明:val onClick: () -> Unit = ......
。
这些类型具有同 函数签名 对应的特殊表示法,即其 参数和返回值:
- 函数类型:
(A, B) -> C
,均有 () 括起来的 参数列表 与 一个返回类型。参数类型列表可为空:() -> C
。Unit
返回类型不可省略。 - 函数类型 可有一个额外的 接收者 类型,表示为:
类型 A.(B) -> C
,表示在 A 的接收者对象上 传递一个 B 类型参数饼干返回一个 C 类型的值。[没明白 接收者对象 ???] -
挂起函数 属于特殊种类的函数类型,表示法中有一个
suspend
修饰符,如:suspend () -> Unit
或suspend A.(B) -> C
。
函数类型表示法可以选择性地加上参数名 x, y:(x: Int, y: Int) -> Point
。这些参数名称可用于 表明参数的含义(代码易读)。
Tips:
- 若需将 函数类型 指定为 可空,可使用圆括号:
((Int, Int) -> Int)?
。 - 函数类型 可用圆括号进行结合:
(Int) -> ((Int) -> Unit)
。 - 箭头是右结合,(Int) -> (Int) -> Unit 等价于 上述,但不等价于 ((Int) -> (Int)) -> Unit
使用 类型别名 给函数起一个别称:
typealias ClickHandler = (Button, ClickEvent) -> Unit
3. 函数类型实例化
有几种方法可获得函数类型的实例。
方式一:使用 函数字面值的代码块,采用以下形式之一:
-
lambda 表达式:
{ a, b -> a + b }
-
匿名函数:
fun(s: String): Int { return s.toIntOrNull() ?: 0 }
备注:带有接收者的函数字面值 可用作 带有接收者的函数类型的值。
方式二:使用 已有声明的可调用引用:
- 顶层、局部、成员、扩展函数:
::isOdd
、String::toInt
- 顶层、成员、扩展属性:
List<Int>::size
- 构造函数:
::Regex
备注:包括 指向特定实例成员的 绑定的可调用引用:foo::toString
。(这里的概念有点生,不态理解!!!)
方式三:使用 实现函数类型接口 的自定义类的实例:
class IntTransformer: (Int) -> Int {
override operator fun invoke(x: Int): Int = TODO()
}
val intFun: (Int) -> Int - IntTransformer() // 函数类型,可以作为接口被类实现,创建的类实例 为 函数类型 实例。
4. 函数类型实例调用
- 获取 函数类型的值:
invole(...) 操作符
调用:f.invoke(x)
或 直接f(x)
。
fun demo02() {
val strPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus // 带有接收者 的函数类型
println(strPlus.invoke("<-", "->"))
println(strPlus("Hello ", "world!"))
println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3)) // 类扩展调用
}
5. Lambda 表达式
lambda 表示式 与 匿名函数 是 “函数字面值”,即 未声明的函数,可作为 表达式 传递。
5.1 Lambda 表达式语法
完整语法形式:
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y}
// 省略 函数类型 写法
val sum1 = { x: Int, y: Int -> x + y}
- lambda 表达式 总是在
{}
中; - 完整语法形式 的 参数声明是放在
{}
内,并有可选的类型标注,函数体在->
符号之后。 - 若推断出 lambda 的返回类型不是
Unit
,则其主体中的最后一个 表达式 会作为返回值。
5.2 传递末尾的 lambda 表达式
- 若函数的最后一个参数是 函数,则传递 lambda 表达式时可以放在圆括号之外:
val product = items.fold(1) { acc, e -> acc * e }
上述语法称为「拖尾 lambda 表达式」。
- 若该 lambda 表达式是调用时唯一的参数,则圆括号可以完全省略:
run { println("...") }
。
5.3 it: 单个参数的隐式名称
- 一个 lambda 只有一个参数是很常见的。
如果编译期可以识别签名,也可以不声明唯一的参数并忽略 -> 。该参数会隐式 声明为 it :
val ints = intArrayOf(1, 2)
ints.filter {
val shouldFilter = it > 0 // 隐式返回最后一个表达式的值
shouldFilter
}
6. 匿名函数
定义:
- 省略了函数名称
- 参数 和 返回类型 同常规函数一致,能根据上下文推断出的参数类型可省略
- 返回类型机制:同正常函数一致。1)对于具有 表达式函数体 的匿名函数将自动过推断返回类型;2)对具有代码块函数体的返回类型 必须显式制定个(或假定为 Unit)。
形式:fun(x: Int, y: Int): Int = x + y
Tips:匿名函数参数 总是在括号内传递。将函数 留在圆括号外的简写语法 仅适用于 lambda 表达式。
1)VS lambda 表达式:
- lambda 缺少 指定函数的返回类型 的能力。
大多数情况非必要,因为返回类型可自动推断。如果要显式指定,可使用另一种语法:匿名函数。
-「非局部返回」的行为。一个不带标签的 return 语句总是用 fun 关键字声明的函数中返回。
lambda 表达式中的 return 将从包含它的函数返回;而匿名函数中的 return 将从匿名函数自身返回。
7. 闭包
定义:在外部作用域中声明的变量。
- lambda 表达式 和 匿名函数(局部函数、对象表达式),均可访问 闭包。
- 在 lambda 表达式中,可以修改 闭包中捕获的变量。
fun demo04() {
var sum = 0
var ints = intArrayOf(1, 2, 3)
ints.filter { it > 0 }.forEach { sum += it }
println(sum)
}
8. 带有接收者的函数字面值
定义:类型 A.(B) -> C
这种形式 的函数字面值实例化。
fun List<String>.demo05(words: MutableList<String>, maxLen: Int) {
this.filter {
it.length <= maxLen
}
val articles = setOf("a", "A", "an", "An", "the", "The")
words -= articles
}
fun callDemo01() {
val words = "A long time ago in a galaxy far far away".split(" ")
val shortWords = mutableListOf<String>()
words.demo05(shortWords, 3) // 在一个类定义另一个类的方法,并可直接调用
println(shortWords)
}
9. 内联函数
TODO
待理解概念与使用场景:
- 带有接收者的函数字面值
- 内联函数