- Lambda 表达式标准语法格式
- 使用Lambda 表达式
- Lambda 表达式简化写法
- ✔️参数类型推导简化
- ✔️使用尾随 Lambda 表达式
- ✔️省略参数声明
- Lambda 表达式与 return 语句
Lambda 表达式是一种匿名函数,可以作为表达式、函数参数 和 函数返回值 使用。
一、Lambda 表达式标准语法格式
Lambda 表达式的语法很灵活,下面只是它的标准语法格式:
{ 参数列表 ->
Lambda 体
}
Lambda 表达式的参数列表与函数的参数列表形式类似,但是 Lambda 表达式参数列表前后 没有小括号() 。箭头符号将参数列表和 Lambda 体分隔开,Lambda 表达式不需要声明返回类型。Lambda 表达式可以有返回值,如果 没有 return 语句,Lambda 体的最后一个表达式就是 Lambda 表达式的返回值;如果 有 return 语句,返回值是 return 语句后面的表达式。
fun calculate(opr: Char): (Int, Int) -> Int = when (opr) {
'+' -> { a: Int, b: Int -> a + b } // Lambda表达式
'-' -> { a: Int, b: Int -> a - b } // Lambda表达式
'*' -> { a: Int, b: Int -> a * b } // Lambda表达式
else -> { a: Int, b: Int -> a / b } // Lambda表达式
}
fun main(args: Array<String>) {
val f1 = calculate('+')
println(f1(10, 5)) // 15
val f2 = calculate('-')
println(f2(10, 5)) // 5
val f3 = calculate('*')
println(f3(10, 5)) // 50
val f4 = calculate('/')
println(f4(10, 5)) // 2
}
上面代码fun calculate(opr: Char)后面的(Int, Int) -> Int是返回值类型(函数类型),而不是 Lambda 表达式。
提示:Lambda 表达式与函数、匿名函数一样都有函数类型,但从 Lambda 表达式的定义中只能看到参数类型,看不到返回类型声明,那是因为返回类型可以通过上下文推导出来。
二、使用Lambda 表达式
Lambda 表达式也是函数类型,可以声明变量,也可以作为其他函数的参数或者返回值使用。
fun calculatePrint(a: Int, b: Int, opr: Char, func: (Int, Int) -> Int) {
println("$a $opr $b = ${func(a, b)}")
}
fun main(args: Array<String>) {
calculatePrint(10, 5, '+', { a, b -> a + b }) // Lambda作为参数
calculatePrint(10, 5, '-', func = { a, b -> a - b }) // Lambda作为参数(命名参数方式传参)
}
三、Lambda 表达式简化写法
kotlin 提供了多种 Lambda 表达式的简化写法。
1、✔️参数类型推导简化
kotlin 编译期可以根据上下文环境推导出参数类型和返回值类型。例如上面代码定义的高阶函数 calculate(opr: Char) 返回值类型为 (Int, Int) -> Int,下面给出此高阶函数的 Lambda 表达式的标准版 和 简化版:
// 标准类型的Lambda表达式
{ a: Int, b: Int -> a + b}
kotlin 可以根据高阶函数calculate的返回值类型(Int, Int) -> Int,推断出 Lambda 表达式参数为 Int 类型,返回值类型也为 Int 类型。
// 简化版
{ a, b -> a + b}
完整的简化版 calculate 代码:
fun calculate(opr: Char): (Int, Int) -> Int = when (opr) {
'+' -> { a, b -> a + b }
'-' -> { a, b -> a - b }
'*' -> { a, b -> a * b }
else -> { a, b -> a / b }
}
2、✔️使用尾随 Lambda 表达式
如果一个函数的最后一个参数是 Lambda 表达式,那么这个 Lambda 表达式可以放到函数括号之后。
fun calculatePrint(a: Int, b: Int, opr: Char, func: (Int, Int) -> Int) {
println("$a $opr $b = ${func(a, b)}")
}
fun calculatePrint105(func: (Int, Int) -> Int) {
println("${func(10, 5)}")
}
fun main(args: Array<String>) {
// 标准调用
calculatePrint(10, 5, '+', { a, b -> a + b })
calculatePrint105({ a, b -> a + b })
// 尾随Lambda表达式
calculatePrint(10, 5, '+') { a, b -> a + b } // 1️⃣
calculatePrint105() { a, b -> a + b } // 2️⃣
// calculatePrint105(){a, b -> a + b} 再简化版本
calculatePrint105 { a, b -> a + b } // 尾随Lambda表达式,如果没有参数可以省略括号
}
注意:尾随 Lambda 表达式容易被误认为是函数声明,如上述代码第1️⃣、2️⃣行,是不是会认为是一个函数?但它缺少函数声明的必要关键字 fun,同时也缺少参数类型,命名规定等。
3、✔️省略参数声明
如果 Lambda 表达式的参数只有一个,并且能够根据上下文环境推导出它的数据类型,那么这个参数声明可以省略,在 Lambda 体中使用隐式参数 it 替代 Lambda 表达式的参数。
fun reversePrint(word: String, func: (String) -> String) {
println("${func(word)}")
}
fun main(args: Array<String>) {
// 未省略参数说明
reversePrint("hello") { word -> word.reversed() }
// 省略参数说明
reversePrint("hello") { it.reversed() }
val result1: (String) -> Unit = { print(it) } // 3️⃣省略参数说明,并且可以根据result1声明的函数类型,判断出隐式参数it的类型
val result2 = { a: String -> print(a) } // 4️⃣不能省略参数说明,无法根据上下环境判断出参数类型
}
注意:Lambda体中 it 隐式变量是由 kotlin 编译器生成的,它的使用有两个前提:一是 Lambda 表达式只有一个参数,二是根据上下文环境能够推导出参数类型。比较代码第3️⃣、4️⃣行会发现,代码第4️⃣行由于 result2 没有指定数据类型,编辑器不能推导出 Lambda 表达式的参数类型,所以不能使用 it。而代码第3️⃣行,由于 result1 指定了数据类型为 (String) -> Unit,编辑器能够推导出 Lambda 表达式的参数类型,所以可以使用 it。
四、Lambda 表达式与 return 语句
Lambda 表达式可以使用 return 语句,它会使程序跳出 Lambda 表达式体;
例如:函数 sum 内部有一个 Lambda 表达式,在 Lambda 表达式内执行 return 语句,则会直接结束 sum 函数,而 return语句的返回值,作为了 sum 函数的返回值。
fun sum(vararg num: Int): Int {
var total = 0
num.forEach {
if (it == 10) return -1
total += it
}
return total
}
fun main(args: Array<String>) {
val n = sum(1, 2, 10, 3)
println(n)
}
2019-06-05 12:48:22.000 17599-17599/cn.ak.kot I/System.out: -1
那么像上面的代码能通过 return 结束 Lambda 表达式吗?当然是可以的,但需要在 return 时指定返回标签,返回标签用法在Kotlin基础认识 (8)程序流程控制中有介绍。修改后的代码如下:
fun sum(vararg num: Int): Int {
var total = 0
num.forEach {
if (it == 10) return@forEach // 5️⃣@forEach是隐式声明标签
total += it
}
return total
}
fun main(args: Array<String>) {
val n = sum(1, 2, 10, 3)
println(n)
}
2019-06-05 14:18:09.269 22302-22302/cn.ak.kot I/System.out: 6
上述代码第5️⃣行是使用隐式标签 @forEach 结束本次 Lambda 表达式运行。
提示:forEach 是集合、数组或区间的函数,它后面是一个 Lambda 表达式,集合、数组或区间对象调用 forEach 函数时,会将它们的每一个元素传递给 Lambda 表达式并执行。
下面是一个显示使用标签的案例:
fun main(args: Array<String>) {
val add = label@ { // 打一个标签,标记结束位置
val a = 1
val b = 2
return@label 10 // 结束运行,返回到标签位置
a + b // 这句代码实际是永远执行不到的
}
println(add())
}
2019-06-05 14:25:10.968 23002-23002/cn.ak.kot I/System.out: 10







网友评论