一、lambda表达式的语法

{parameter list -> body}

kotlin和java的lambda表达式的语法存在一定程度的差异:

1、kotlin在通常的情况下不允许有return语句,而java则可以有零个或者多个return语句

2、kotlin有隐式的参数it:fun isPrime(n: Int) = n > 1 && (2 until n).none({n % it == 0})

3、lambda的语法格式:fun walkTo(n: Int, action: (Int) -> Unit) = (1..n).forEach(action); 该函数接收的action参数是一个lambda表达式,该表达式接受一个int类型的参数,并且返回值是Unit; 在kotlin中,Unit就是java里面的void

4、kotlin和java的一个共通点是lambda尾随的特性:如上的walkTo可以有如下的调用:walkTo(5) {i -> print(i)}

5、lambda可以作为函数的返回值返回

6、lambda有闭包的概念,可以捕获外部的变量

var factor = 2
val doubled = listOf(1, 2).map {it * factor}
factor = 0
doubled.forEach{println(it)}// 输出 2, 4

如上在map的lambda中捕获了factor,此时的factor是2,所以该lambda表达式捕获到了factor等于2的值;接下去虽然factor=0,但是已经过了lambda的区域;虽然最后doubled的输出是在factor=0之后

7、lambda在特殊情况下带return语句

lambda有两种情况可以带return,一种是使用标签的方式,一种是内联的方式

这里说下使用标签的方式

fun invokeWith(n: Int, action: (Int) -> Unit) {
    println("enter invokeWith $n")
    action(n)
    println("exit invokeWith $n")
}
fun caller() {
    (1..3).forEach{ i ->
        invokeWith(i) here@ {
            println("enter for $it")
            if (it == 2) {
                return@here
            }
            println("exit for $it")
        }
    }
    println("end of caller")
}
caller()

如上的输出如下所示
/*
enter invokeWith 1
enter for 1
exit for 1
exit invokeWith 1
enter invokeWith 2
enter for 2
exit invokeWith 2
enter invokeWith 3
enter for 3
exit for 3
exit invokeWith 3
end of caller
*/

通过上面可以看出,return@here只是当it==2时候返回到了这个invokeWith(i)的标记位置,这里return@invokeWith也可以产生相同的效果

 

8、lambda的内联

lambda的内联是将lambda函数对应的字节复制到所在函数的调用位置,而不止是通过函数指针去调用远处的某个函数

回到第5条关于lambda作为函数返回:内联的lambda函数不能作为返回值返回;因为内联的lambda严格来说不算是有函数指针,返回值没办法返回对应函数内的某块内存

回到第6条关于捕获外部变量:

var factor = 2
val doubledAlso = sequenceOf(1,2).map{it * factor}
factor = 0
doubledAlso.forEach{println(it)}
如上的输出是
0
0

由于sequenceOf对应的lambda是内联的,所以这里对应的其实是该lambda的一整块内存,lambda中的factor对应的是外部factor的地址,所以当factor后面设置为0再调用显示的时候则lambda中的factor也是为0

回到第7条关于lambda内部使用return

fun caller1() {
    (1..3).forEach {i ->
        println("in foreach for $i")
        if (i == 2) {
            return
        }

        invokeWith(i) {
            println("enter for $it")
            if (it == 2) {
                return@invokeWith
            }
            println("exit for $it")
        }
    }
}
caller1()
调用的结果是:
in foreach for 1
enter invokeWith 1
enter for 1
exit for 1
exit invokeWith 1
in foreach for 2

如上所示,在i==2的时候整个forEach的循环调用被return掉了

所以内联其实就是将整个函数拷贝到使用的内联的位置,然后在那里做调用;该位置的return相当于整个函数的return

8、inline对应的noinline

刚才在关于第5点的内容中说的内联lambda不能作为返回值,但是如果是内联函数,但是要作为返回值该如何呢? 这里可以使用noinline

inline fun invokeTwo(n: Int, action1: (Int) -> Unit, noinline action2: (Int) -> Unit): (Int) -> Unit {
    println("enter invokeTwo $n")
    action1(n)
    println("exit invokeTwo $n")
    return action2
}

如上所示,在invokeTwo设置为inline的时候,将action2标记为noinline则可以将action2当作返回值返回否则不可以

9、inline对应的crossinline

如果我们既要将lambda当成内联函数,又要将该lambda返回,则可以使用crossinline关键字

inline fun invokeTwo(n: Int, action1: (Int) -> Unit, crossinline action2: (Int) -> Unit): (Int) -> Unit {
    println("enter invokeTwo $n")
    action1(n)
    println("exit invokeTwo $n")
    return {input: Int -> action2(input)} // 既有return又要返回,则可以使用crossinline来做标记
}

fun callInvokeTwo() {
    invokeTwo(1, {i ->
    if (i == 1) {return}
    report(i)}, {i ->
    if (i == 2) {return} // 这里使用了return所以需要用inline
    report(i)})
}
callInvokeTwo()

 

原文地址:http://www.cnblogs.com/czwlinux/p/16879272.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性