Lambda定义
lambda 表达式总是括在花括号中, 完整语法形式的参数声明放在花括号内,并有可选的类型标注, 函数体跟在一个 ->
符号之后。如果推断出的该 lambda 的返回类型不是 Unit
,那么该 lambda 主体中的最后一个(或可能是单个) 表达式会视为返回值。
意思就是说lambda表达式一定要放在花括号 { } 中 , ->
前面是参数,后面是方法体就是你要拿这个参数进行的操作。如果你的lambda是有返回值的,->
后面最后一个表达式会被视为返回值,代码如下:
Lambda就像一个简化的函数,如下:
lam1 对应的就是method1 ,两个参数,返回一个Int值。x+y 作为 -> 后面最后一个表达式返回
var lam1: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
fun method1(x: Int, y: Int): Int {
return x + y
}
lam2 对应的就是method2,如果表达式不需要返回值,x+y 只是做了一次运算,并没有返回
var lam2: (Int, Int) -> Unit = { x: Int, y: Int -> x + y }
fun method2(x: Int, y: Int): Unit {
x + y
}
lambda 主体中的最后一个(或可能是单个) 表达式会视为返回值。为啥非要这么说呢
var lam1: (Int, Int) -> Int = { x: Int, y: Int ->
1操作 ➺ val sum = x + y
2操作 ➺ println(sum)
3操作 ➺ sum
}
注意:Kotlin不像java用 ; 作为结束标志,所以要每行写一个操作,如果放在一行编译就会报错!
->
后面有 1 ,2 , 3 三个操作,最后3处的表达式作为返回值返回。
那如果
var lam = { x: Int, y: Int -> x + y }
前面声明不指定有没有返回值,x+y 会不会作为返回值返回呢?
var lam = { x: Int, y: Int -> x + y }
fun main() {
println(lam(2, 3))
}
打印结果:
D:\AndroidStudio\android-studio\jre\bin\java.exe -
5
Process finished with exit code 0
结果是会作为返回值,如果不指定,会根据->
后面返回表达式自动推算返回值,代码如下
修改1:
var lam = { x: Int, y: Int -> x > y }
打印结果
D:\AndroidStudio\android-studio\jre\bin\java.exe -
false
Process finished with exit code 0
修改2:
var lam = { x: Int, y: Int -> "Result $x $y" }
打印结果
D:\AndroidStudio\android-studio\jre\bin\java.exe -
Result 2 3
Process finished with exit code 0
=======================================================================
Lambda规则
我们通常看到的lambda印象最深就是,精简,最后简化到只剩一个 { } 都可以,虽然看不懂,就好厉害的感觉
那如何将一个函数方法改成一个lambda表达式呢?这就和下面lambda非常重要的几个规则有关
1 在 Kotlin 中有一个约定:如果函数的最后一个参数是函数,那么作为相应参数传入的 lambda 表达式可以放在圆括号之外 ,这种语法也称为拖尾 lambda 表达式。
fun main() {
method3( "hello" , { x -> x + 6 } ) : 正常调用,
↓
↓
↓ 根据拖尾原则,如果lambda表达式在参数的最后一个位置
↓ 可以拿到圆括号外面
↓
method3("hello") { x -> x + 6 }
}
fun method3(msg: String, lamParams: (Int) -> Int) {
两个参数:一个String,一个lambda表达式
}
2 如果一个 lambda 表达式只有一个参数,可以省略这个参数并忽略 ->。而这个被省略的参数可以用it代替
还是接着上面的代码继续修改
fun main() {
method3( "hello" , { x -> x + 6 } ) : 正常调用,
↓
↓
↓ 根据拖尾原则,如果lambda表达式在参数的最后一个位置
↓ 可以拿到圆括号外面
↓
method3("hello") { x -> x + 6 }
↓
↓ 根据单参数省略原则:
↓ 参数 x 省略,
↓ -> 省略
↓ x可用 it 代替
↓
method3("hello") { it + 6 }
}
fun method3(msg: String, lamParams: (Int) -> Int) {
两个参数:一个String,一个lambda表达式
}
这样经过简化的之后,看起来很厉害的样子就出来了: method3("hello") { it + 6 }
3 如果 lambda 表达式的参数未使用,那么可以用下划线取代其名称:
fun main() {
method("") { _, str -> str }
分析:
1 lambda表达式位置在method方法中参数最后一个可以拿到括号外面
2 lambda表达式有两个参数,所以不能省略
3 Int参数 在箭头后面的操作中没有用到,可以用 _ 代替
}
fun method(msg: String, lam: (Int, String) -> String) {
}
这样就可以将函数精简为Lambda表达式了。
fun method5(str: String, x: Int, bool: Boolean) {
str + x + bool
}
↓
↓ 精简
↓
{ str: String, x: Int, bool: Boolean -> str + x + bool }
然后将这个Lambda表达式赋值给lam5
var lam5 = { str: String, x: Int, bool: Boolean -> str + x + bool }
main(){
method6是用method5 的函数形式作为参数
我们分别传递 method5 和 lambda5 到method6中,完全没有问题,说明精简是对的
method6(lam5)
method6(::method5)
}
fun method6(m: (String, Int, Boolean) -> Unit) {
参数是(String, Int, Boolean) -> Unit这么一个表达式
}
=======================================================================
Lambda显示声明返回值:
Lambda 禁止直接使用return关键字,我们可以使用限定的返回语法: return@函数名 从Lambda 显式返回一个值。 否则,将隐 式返回最后一个表达式的值。
lambda默认将 -> 最后一个表达式作为返回值返回
method("") { _, str -> str }
也可以显示的 return 返回值
method("") { _, str -> return@method str }
=======================================================================
匿名函数:
当想要显示指定lambda 表达式的返回类型,我们可以使用匿名函数的形式
fun(x: Int, y: Int): Int = x + y
fun关键词后面并没有和普通函数一样有一个函数名,所以叫匿名函数。
而且如果返回值可以推算得出返回值也是可以省略的,即:fun(x: Int, y: Int) = x + y 也是可以的
注意:
1 匿名函数参数必须放在括号里面,不能像lambda表达式一样简化处理。
2 Lambda表达式与匿名函数之间的另一个区别是非局部返回的行为:这是什么意思呢?
官方文档是这样说的:一个不带标签的 return 语句总是在用 fun 关键字声明的函数中返回。这意味着 lambda 表达式中的 return 将从包含它的函数返回,而匿名函数中的 return 将从匿名函数自身返回。
非局部返回,这就牵扯到了 inline 内联函数
说的是 内联函数 如果被处理成了Lambda表达式的时候,return 返回的是外部那个调用内联函数的函数,而不是内联函数本身。(注意:前面说过Lambda是禁止直接使用return关键字,要使用必须是:return@函数名 这种形式。但是内联函数使用Lambda就可以直接使用 return关键字)。看代码:
fun song(f: (String) -> Unit) {
}
inline fun speak(f: (String) -> Unit) {
}
fun behavior() {
song {
println("song $it")
return //此处报错: 'return' is not allowed here
}
speak {
println("speak $it")
return //此处没问题
}
}
fun main() {
behavior()
}
song() 方法没有内联不允许在 Lambda 中直接 return 所以报错。
而我们函数调用顺序为
main() -> behavior() -> speak
这样我们在 speak的参数lambda表达式里面 return,结束的是哪个函数呢?
结束的是 behavior 函数。因为 speak 是内联函数。编译的时候就变成下面样子,。
调用处大体示意: 编译时大体示意:
fun main() { fun main() {
behavior { behavior {
speak {
println("speak $it") println("speak $it")
return return
}
} }
} }
所以 return 的就是 behavior 函数了。
(注:内联函数能不能return还有其他限制,这里就不说了。)
上面说 lambda 表达式中的 return 将从包含它的函数返回,而匿名函数中的 return 将从匿名函数自身返回。说的是Lambda在内联函数的情况下。
============================================================================
最近看一个库源码发现lambda反编译成java是这样的:
fun t() = {
s: String, s2: String, s3: String, s4: String,
s5: String, s6: String, s7: String, s8: String,
s9: String, s10: String, s11: String, s12: String,
s13: String, s14: String, s15: String, s16: String,
s17: String, s18: String, s19: String, s20: String,
s21: String, s22: String
->
Unit
}
public final Function22 t() {
return (Function22)null.INSTANCE;
}