上一篇,我们学习了Kotlin中的普通函数,今天继续来学习Kotlin中的函数。由于Kotlin中支持高阶函数语法,所以函数我们分为三篇来学习,今天是第二篇:高阶函数和Lambda表达式。
高阶函数是将函数用作参数或返回值的函数。
// sumBy函数的源码
public inline fun CharSequence.sumBy(selector: (Char) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
示例:
val testStr = "abc"
val sum = testStr.sumBy { it.toInt() }
println(sum)//返回结果294,因为字符a对应的值为97,b对应98,c对应99,故而该值即为 97 + 98 + 99 = 294
fun test(body: () -> Int): Int{
return body()
}
示例:
val testResult = Test().test { 'a'.toInt() }
println("result : $testResult")
//结果是result : 97
传入两个参数,并传入一个函数来实现他们不同的运算逻辑
private fun mathByFun(num1 : Int , num2 : Int , result : (Int ,Int) -> Int) : Int{
return result(num1,num2)
}
private fun mathOpt() {
//按照mathByFun函数中的参数顺序应该是下面的写法,但是Lambda表达式作为最后一个参数的时候可以挪出括号外,这里说明下
/*val resultAdd = mathByFun(1,2, {
num1, num2 -> num1 + num2
})*/
val resultAdd = mathByFun(1,2){
num1, num2 -> num1 + num2
}
val resultSub = mathByFun(3,4){
num1, num2 -> num1 - num2
}
val resultMul = mathByFun(5,6){
num1, num2 -> num1 * num2
}
val resultDiv = mathByFun(6,3){
num1, num2 -> num1 / num2
}
println("resultAdd = $resultAdd")
println("resultSub = $resultSub")
println("resultMul = $resultMul")
println("resultDiv = $resultDiv")
}
可以看到在mathOpt函数中,调用mathByFun函数,传入不同函数体从而实现了不同的运算逻辑。
lambda 表达式与匿名函数是“函数字面值”,即未声明的函数, 但立即做为表达式传递。考虑下面的例子:
max(strings, { a, b -> a.length < b.length })
函数 max 是一个高阶函数,它接受一个函数作为第二个参数。 其第二个参数是一个表达式,它本身是一个函数,即函数字面值,它等价于以下具名函数:
fun compare(a: String, b: String): Boolean = a.length < b.length
Lambda 表达式的完整语法形式如下:
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
lambda 表达式总是括在花括号中, 完整语法形式的参数声明放在花括号内,并有可选的类型标注, 函数体跟在一个 -> 符号之后。如果推断出的该 lambda 的返回类型不是 Unit,那么该 lambda 主体中的最后一个(或可能是单个) 表达式会视为返回值。
如果我们把所有可选标注都留下,看起来如下:
val sum = { x: Int, y: Int -> x + y }
在 Kotlin 中有一个约定:如果函数的最后一个参数是函数,那么作为相应参数传入的 lambda 表达式可以放在圆括号之外(这个在上面的例子中也有提过):
val product = items.fold(1) { acc, e -> acc * e }
如果该 lambda 表达式是调用时唯一的参数,那么圆括号可以完全省略:
run { println("...") }
一个 lambda 表达式只有一个参数是很常见的。
如果编译器自己可以识别出签名,也可以不用声明唯一的参数并忽略 ->。 该参数会隐式声明为 it:
val ints = listOf(1,2,3,-1)
ints.filter { it > 0 } // 这个字面值是“(it: Int) -> Boolean”类型的
我们可以使用限定的返回语法从 lambda 显式返回一个值。 否则,将隐式返回最后一个表达式的值。
因此,以下两个片段是等价的:
ints.filter {
val shouldFilter = it > 0
shouldFilter
}
ints.filter {
val shouldFilter = it > 0
return@filter shouldFilter
}
如果 lambda 表达式的参数未使用,那么可以用下划线取代其名称:
//这里没有用到num1这个参数,可以用_来替代
val resultAdd = mathByFun(1,2) {
_, num2 -> num2
}
上面提供的 lambda 表达式语法缺少的一个东西是指定函数的返回类型的能力。在大多数情况下,这是不必要的。因为返回类型可以自动推断出来。然而,如果确实需要显式指定,可以使用另一种语法: 匿名函数 。
fun(x: Int, y: Int): Int = x + y
匿名函数看起来非常像一个常规函数声明,除了其名称省略了。其函数体可以是表达式(如上所示)或代码块:
fun(x: Int, y: Int): Int {
return x + y
}
参数和返回类型的指定方式与常规函数相同,除了能够从上下文推断出的参数类型可以省略:
ints.filter(fun(item) = item > 0)
匿名函数的返回类型推断机制与正常函数一样:对于具有表达式函数体的匿名函数将自动推断返回类型,而具有代码块函数体的返回类型必须显式指定(或者已假定为 Unit)。
请注意,匿名函数参数总是在括号内传递。 允许将函数留在圆括号外的简写语法仅适用于 lambda 表达式。
Lambda表达式与匿名函数之间的另一个区别是非局部返回的行为。一个不带标签的 return 语句总是在用 fun 关键字声明的函数中返回。这意味着 lambda 表达式中的 return 将从包含它的函数返回,而匿名函数中的 return 将从匿名函数自身返回。
我们都知道,程序的变量分为全局变量和局部变量,全局变量,顾名思义,其作用域是当前文件甚至文件外的所有地方;而局部变量,我们只能再其有限的作用域里获取。
那么,如何在外部调用局部变量呢?答案就是——闭包,与此给闭包下个定义:闭包就是能够读取其他函数内部变量的函数
看示例:
fun makeFun():()->Unit{
var conut = 0
return fun(){ //返回一个匿名函数,这个函数持有count的状态
println(++conut)
//可以定义函数
//fun test1(){}
//可以定义类
//class Test2{}
}
}
fun main(args: Array<String>) {
val makeFun = makeFun() //函数调用,返回一个函数
makeFun() //调用这个返回的函数,此时makeFun持有makeFun()内部变量的状态
makeFun()
makeFun()
}
//返回结果1 2 3
今天的学习笔记就先到这里了,下一篇我们将学习Kotlin中的内联函数。
老规矩,喜欢我的文章,欢迎素质三连:点赞,评论,关注,谢谢大家!