Kotlin学习笔记 – Lambda表达式

Lambda表达式

Lambda表达式其实就是匿名函数。

在Kotlin中,函数也是作为类型的一种出现的,可以被赋值和传递。

这一点被Lambda表达式发扬光大。

如果我们把上一次的sum函数用Lambda表达式的方式来定义。

snipaste_20170817_224750

Lambda表达式也可以不带->。

snipaste_20170817_224837

Lambda表达式可以有很多行。

表达式的值是最后一行的值。

snipaste_20170817_225032

但是Lambda表达式不可以对参数进行修改。

因为这是Value-Parameter。

Val cannot be reassigned。

这里val不是var。

snipaste_20170817_225124

这个Lambda表达式的类型是(Int, Int) -> Int。

意思是传2个Int返回一个Int的类型。

Lambda表达式的使用是直接使用。

snipaste_20170817_225407

注意到输出的结果是

1
Hello
4

1是

println("$arg1")

输出的;

Hello是

println("Hello")

输出的;

而4是

println(moreLines(1,3))

输出的。

这也就是说明了Lambda表达式的值是最后一行的值。

在使用的时候,注意到Lambda表达式后面有一个括号。

括号应该是一个变量对某些方法的调用。

那么调用的是哪个方法呢?

其实,Lambda()相当于Lambda.invoke()。

也就是说moreLines(1,3)就是moreLines.invoke(1,3)。

这是运算符重载的一个方法,这个以后再说。

参考Java反射中的invoke。

数组的遍历与Java是类似的。

snipaste_20170817_225850

或者也可以这么写。

snipaste_20170817_225947

这里第一种写法的s是可以随便自己取名的,第二种写法只能是it指代forEach循环的iterator,不可以自己取名字。

snipaste_20170817_230122

直接点进去看源代码。

看到forEach是Array的一个扩展方法。

snipaste_20170817_230211

这个方法传了一个action。

这个action的类型是一个Lambda表达式的类型(T) -> Unit。

表示接受一个T返回一个Unit。

然后里面做的事情是对于this的每一个元素做action这个动作。

this就是这个Array。

action就是我们传进去的println。

action调用action(element)就是println(element)。

这个写法其实和上面的写法是一样的。

这么写可能有点奇怪。

那就写完整。

snipaste_20170817_230747

forEach接受一个Lambda表达式。

这个表达式要接受一个T但是返回是Unit。

我们定义一个Lambda表达式类型是(T) -> Unit,也就是aLambda。

这个表达式接受了一个String然后把它输出,即(String) -> Unit。

注意到变量名,在forEach里不能取名而在其他地方可以随便取名字。

那么化简以后,其实就是用上面第一行等号右边代替下面第二行Lambda表达式的调用。

snipaste_20170817_231422

这个时候是可以自己取名字的。

但是如果省略掉->左边部分的类型,那么就要用到it来指代。

snipaste_20170817_231723

而只有一个参数的时候,it也是可以省略的。

snipaste_20170817_231301

注意到这里的it已经加粗了。

再然后,如果最后一个参数是Lambda表达式,那么Lambda表达式可以移到括号的右边。

snipaste_20170817_231906

如果这个时候括号里面已经什么参数都没有了,那么括号也可以省掉。

剩下的就是我们最开始写的代码。

其实还可以简化。

如果Lambda表达式的类型和action的类型完全一样,那么整个Lambda表达式就可以直接作为引用传递到forEach。

这里Lambda表达式的类型是(String) -> Unit,action的类型是(T) -> Unit。

snipaste_20170817_232235

这是最终的版本。

snipaste_20170817_232529

如果输入的是a b c d q e f g h。

那么输出的是a b c d。

这和我们期望的遇到q退出是一样的。

但是没有输出THE END。

这是因为这是一个表达式而不是函数。

是表达式的话,return的就是main了。

如果真的想要中断表达式,那要用到标签。

snipaste_20170817_232741

但是实际上这并不会真正把forEach给终止了。

如果把if放在后面,会发现还是会继续执行的,只不过是把这一次Lambda表达式的调用给返回了。

Anyway,我们想要的结果已经得到了。

如果要真正中断,那么就要再写一个标签了。

判断Lambda表达式的类型。

val sum = { arg1:Int, arg2:Int -> arg1 + arg2 }
// (Int, Int) -> Int

val printHello = { println("Hello") }
// () -> Unit

之前一次写过的还有int2Long,它的类型(Int) -> Long。

注意println()的类型是(Any?) -> Unit,main()的类型是(Array<String>) -> Unit。

用println()打印类型。

println(sum)

snipaste_20170817_233318

(kotlin.Int, kotlin.Int) -> kotlin.Int与刚才说到的(Int, Int) -> Int是一样的。

可见我们的理解是正确的。

如果是Kotlin的早期版本,或者也可能出现这样的结果。

Function2<java.lang.Integer, java.lang.Integer, java.lang.Integer>

这个名字叫做Function2,还带泛型参数。

snipaste_20170817_233445

意思是P1,P2,R。

表示Parameter1和2,以及Return。

snipaste_20170817_233718 snipaste_20170817_233822

如果想要printHello的类型.

println(::printHello)

snipaste_20170817_234821

会输出

val printHello: () -> kotlin.Unit

snipaste_20170817_234818

类型是() -> Unit。

在Kotlin的早期版本,如果这样会报错,说引用不能被使用。

function XXX (Kotlin reflection is not avaliable)

snipaste_20170817_234904

snipaste_20170817_234909

那我们可以旁敲侧击一下,看看::printXXX is () -> Unit。

snipaste_20170817_234915

输出的是true。

snipaste_20170817_234922

Function从0到22,一共有23个。

那么如果我接受要23个参数。

snipaste_20170817_234932

会直接Crash掉。

说找不到Function23。

类型真的有,但是找不着。

snipaste_20170817_234939