Kotlin Lambda详解及非局部返回是啥意思?

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;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值