系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正。如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!
Kotlin学习笔记(1)- 环境配置
Kotlin学习笔记(2)- 空安全
Kotlin学习笔记(3)- 语法
Kotlin学习笔记(4)- 流程控制
Kotlin学习笔记(5)- 类
Kotlin学习笔记(6)- 属性
Kotlin学习笔记(7)- 接口
Kotlin学习笔记(8)- 扩展
Kotlin学习笔记(8)- 扩展(续)
Kotlin学习笔记(9)- 数据类
Kotlin学习笔记(10)- 泛型
Kotlin学习笔记(11)- 内部类和嵌套类
Kotlin学习笔记(12)- 委托
Kotlin学习笔记(13)- 函数式编程
Kotlin学习笔记(14)- lambda
在上一篇《Kotlin学习笔记(13)- 函数式编程》的最后我提到lambda为函数式编程提供了更多更好的实现,如果你还不太了解什么是函数式编程,那么可以看看我的上一篇文章。那么我们首先来看看什么是lambda,在百度百科上是这么说的:
“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
一、Lambda的定义
看上面的说法有点抽象,有点不明所以,我们先来看一个栗子
class Num {
fun logic(a: Int, b: Int, calc: (Int, Int) -> Int){
println("calc : ${calc(a,b)}")
}
}
fun main(args : Array<String>){
val num = Num()
num.logic(1, 2, {x,y -> x+y})
}
// 输出
calc : 3
这个栗子和上一篇文章很像,只是在调用的时候改成了Lambda方式:num.logic(1, 2, {x,y -> x+y})
,其中{x,y -> x+y}
就是我们今天要讲的Lambda表达式,它的完整格式应该是这样
{ x: Int, y: Int -> x + y }
写成java代码是这个样子:
public int sum(int x, int y){
return x+y;
}
可以很明显看出有几个规则:
- 参数写在
->
左边,格式与普通函数的参数格式一样,多个参数用逗号,
分割 - 参数的类型可选,可忽略,编辑器会根据上下文推断(这就和普通函数不一样了吧,有种“我知道你懂得,所以就不写了”的感觉有木有,知己的感觉啊……)
- 函数体跟在
->
右边 - Lambda表达式总是被大括号
{}
包围着
二、Lambda中的一些约定
-
如果 Kotlin 可以自己计算出签名,它允许我们不声明唯一的参数,并且将隐含地为我们声明其名称为
it
。其实通常情况下它都可以自己计算出签名,也就是说,如果函数字面值只有一个参数, 那么它的声明可以省略(连同->
),其名称是it
fun oneParams(one : (Int) -> Int){ println("oneParams : ${one(5)}") } fun main(args : Array<String>){ val num = Num() num.oneParams({it * 2}) } // 输出 oneParams : 10
说到省略,其实还有一种情况可以省略
->
,大家应该也能想到,就是无参函数。fun empty(emptyM : () -> Unit){ emptyM() } fun main(args : Array<String>){ val num = Num() num.empty({println("empty method")}) } // 输出 empty method
-
如果Lambda中的某个参数没有用到,可以用下划线
_
代替,也就是说,省了好多请名字的脑细胞有木有!这个特性从1.1开始可以使用,现在你看到的时候应该已经不止1.1了吧,所以这个限制看看就好~fun unusedParams(unused : (Int,Int) -> Int){ println("unusedParams : ${unused(5,10)}") } fun main(args : Array<String>){ val num = Num() num.unusedParams { _, used -> used * 2 } } // 输出 unusedParams : 20
-
如果函数的最后一个参数是一个函数,那么我们在用Lambda表达最后一个函数参数的时候,可以把它放在括号
()
外面,所以下面的写法是等价的。class Num { fun logic(a: Int, b: Int, calc: (Int, Int) -> Int){ println("calc : ${calc(a,b)}") } fun sum(a: Int, b: Int) = a + b } fun main(args : Array<String>){ val num = Num() // 写法1 num.logic(1, 2, {x : Int,y : Int -> x+y}) // 写法2 num.logic(1, 2){x : Int,y : Int -> x+y} // 写法3 num.logic(1, 2){x,y -> x+y} }
那么这么写有什么好处呢?难道只是位置变了一下?当然不是,不要忘记,Lambda的
->
后面是方法体,也就是很多时候不是想栗子中这样只有一行,如果有多行的话,体会一下他们的区别:num.logic(1, 2, {x,y -> println("extra line") x+y }) num.logic(1, 2){x,y -> println("extra line") x+y }
是不是感觉下面的写法要优雅很多,也明确很多?
-
其实在上面一点应该已经能看出,如果有需要的话,Lambda会隐式的返回最后一个表达式的值,就像上面的最后一行
x+y
。当然,我们也可以显示的表达返回值,下面的写法还是一样的:// 写法1 num.unusedParams { _, used -> println("print first") return@unusedParams used * 2 } // 写法2 num.unusedParams { _, used -> println("print first") used * 2 }
三、匿名函数
看了这么多,我们发现一个问题,Lambda的返回类型全是自动推断的,虽然很人性化,但是有时候我们就是想自己指定类型怎么办?当然是有办法的,那就是匿名函数。
fun(x:Int, y:Int):Int{return x+y}
匿名函数看起来非常像一个常规函数声明,除了其名称省略了。其函数体可以是表达式(如上所示)或代码块。由于这已经不算正经的Lambda,所以它不需要被大括号{}
包裹,也不能像Lambda一样写在括号()
外,而调用也是直接调用。
num.logic(1, 2, fun(x:Int, y:Int):Int{return x+y})
四、闭包
Lambda 表达式或者匿名函数(以及局部函数和对象表达式) 可以访问其 闭包 ,即在外部作用域中声明的变量。 与 Java 不同的是可以修改闭包中捕获的变量:
var sum = 0
ints.filter { it > 0 }.forEach {
sum += it
}
print(sum)
五、小结
其实没什么小结,关于更多的函数式的高级应用、复杂应用,个人也正在学习,大家互相交流。