Kotlin之Lambda表达式
什么是lambda
Lambda表达式,简称lambda,是一种包含代码块的对象,注意,它是一个对象。我们可以将其赋给一个变量,就像其他的类型的对象一样,或者将其传递给一个方法,然后该方法就会执行其包含的代码块。也就是说,你可以使用lambda将特定的行为传递给更通用的方法。
lambda实战
lambda表达式的格式
我们先看看如何去书写一个lambda
{ x: Int -> x + 5 }复制代码
以上就是一个最简单的lambda表达式,它将Int参数的值加5 。lambda以花括号作为开始和结束。所有的lambda都被定义在花括号中,并且不可以省略。
在花括号中,使用x:Int
定义了单个Int参数x,lambda可以有单参数,也可以有多个参数或者没有参数。
参数之后是->
,这个符号用于隔离参数和主体,后面的就是主体部分,也就是x+5
,主体部分可以有多行,其中最后一行将作为lambda表达式的返回值。
我们来写一个有多个参数的lambda吧
{a:Int,b:Int -> a+b}复制代码
将lambda赋值给变量
我们可以将lambda表达式赋值给变量,就像其他任何对象进行赋值一样,接着上边的例子:
var addFive = { x: Int -> x + 5 }复制代码
我们定义了一个变量 addFive,在使用lambda对其进行赋值操作的时候,是将代码块赋值给他,而不是将代码的结果赋值给他,要执行lambda中的代码,就需要显示的调用他。
如何进行调用呢?我们可以使用invoke
函数调用lambda,并传入参数的值,
var s = addFive.invoke(5) println(s)复制代码
输出结果为 10 。
也可以直接进行调用
println(addFive(5))复制代码
同样输出10 。
当然无论哪种调用方式,最终生成的字节码都是通过invoke方法进行调用的。
Lambda的表达式类型
同任何其他类型(Int,Double,Float...)一样,lambda也是一种类型,不同点在于,他不会为lambda的实现指定类名,而是指定lambda的参数和返回值类型。 其类型的格式如下
(parameters)-> return_type复制代码
如果你的lambda具有单独的Int参数并返回一个字符串
var l = {x:Int -> "$x"}复制代码
那么,这个lambda的类型就是
(Int)-> String复制代码
如果将lambda赋值给一个变量,编译器会根据该lambda来推测变量的类型。当然我们可以显示的定义该变量的类型,下面我们就来显示的定义一下
//声明变量 指定类型 赋值操作 var v : (Int)->String = {x:Int -> "$x"}复制代码
lambda参数类型的自动解析
显示声明变量类型的时候,可以在lambda中省略任何编译器能够推断出的类型的声明,如以上的显示声明的操作var v : (Int)->String = {x:Int -> "$x"}
,其中,编译器已经从 v 的类型定义中得知,任何赋值给该变量的lambda必须有一个Int参数,这就意味着可以在lambda参数定义中省略Int类型的声明,因为编译器可以推断出其类型,我们可以简写为:
var v : (Int)->String = {x -> "$x"}复制代码
除此之外,如果lambda具有一个单独的参数,并且编译器能够推断出其类型,则可以省略该参数,并在lambda的主体中使用 it 关键字代替它。
还是上面的语句,其中只有一个 Int 类型的参数 x,并且编译器可以推断出其类型,所以我们就可以将该语句简化为
var v : (Int)->String = {"$it"}复制代码
这里要注意的是:只有在编译器能推断该参数类型的前提条件下,才可以使用 it 语法。如果编译器无法推断,则不能使用,下面就是一个错误的例子:
var v = {"$it"}复制代码
将lambda作为函数的参数
除了将lambda赋值给变量,还可以使用一个或者多个lambda作为函数的参数,通过这种方式就允许我们将特定的行为传入其他的函数中。
下面我们就来编写一个带有lambda参数的函数。
// 函数名 Double类型参数 lambda类型的参数 返回值类型 fun convert(x:Double, converter:(Double)->Double): Double { }复制代码
以上就将lambda当作参数传入了另外一个函数中。
函数我们定义好了,如何让lambda的参数在新的函数中发挥作用呢?现在我们给convert函数定义一些功能,让其实现一个数据计算的功能。我们将参数 x 传入之后,通过lambda进行计算,并将结果进行返回。
完整的convert函数的逻辑如下:
fun convert(x:Double,converter:(Double)->Double): Double { //调用converter的lambda,并将其返回值赋值给result val result = converter(x) //返回结果 return result }复制代码
通过以上的分析,我们就可以自己定义这个lambda参数的逻辑,传入函数体,就可以实现相应的需求。
print(convert(10.0, { x:Double-> x+10 } ))复制代码
以上我们传入了一个简单的lambda表达式,实现加10的逻辑,最终的输入结果为 20.0 。
在这里有一些问题需要重点说一下:如果lambda是函数的最后一个参数,就像上面我们定义的convert,那么可以将lambda参数移动到函数调用的括号外面。
print(convert(10.0) {x:Double->x+10} )复制代码
如果函数只有一个参数并且该参数为lambda,则可以在调用该函数时省略掉整个括号,我们修改一下上面的convert函数
fun convert(converter:(Double)->Double): Double { return converter(10.0) }复制代码
现在其只有一个参数,并且参数类型为lambda。我们正常的调用为:
//这样 print(convert( {x:Double -> x+10} )) //或者这样 print(convert() {x:Double -> x+10} )复制代码
这里就可以简写为
print(convert {x:Double -> x+10} )复制代码
将外面的括号去掉。
函数返回值是一个lambda
除了将lambda作为参数,函数还可以通过lambda的类型指定其返回一个lambda。如下:如果传入的x等于0,那么就返回 乘以10 的lambda,否则返回 乘以20的lambda。
fun getLambda(x:Int):(Int)->Int { if(x == 0){ return {m:Int -> m * 10} }else{ return {it * 20} } }复制代码
接下来我们来调用一下这个方法。
val m = getLambda(0) print(m(10)) val n = getLambda(1) print(n(10))复制代码
打印的结果为 100和200 。
将lambda作为函数的参数和返回值
现在我们来创建这样一个函数,接收两个lambda参数进行合并,最后返回一个新的lambda。
fun combine(lambda1:(Double)->Double, lambda2:(Double)->Double) : (Double)->Double{ return {x:Double -> lambda2(lambda1(x))} }复制代码
我们来分析一下这个函数,首先该函数有两个参数 lambda1,lambda2 都是lambda类型的,并且最终都返回一个Double类型的值。
函数的返回值也是lambda,不过其中又结合了lambda1和lambda2的lambda,利用这两个lambda来完成最终的计算。
我们来调用一下这个函数。
//首先定义两个lambda的参数 //✖️2的操作 val l1 = {x:Double -> x*2} //除4的操作 val l2 = {x:Double -> x/4} //得到一个新的lambda val combine = combine(l1,l2) //打印新的lambda的调用结果 println(combine(10.0))复制代码
最终的打印结果为5.0,现将 10 交给l1进行✖️2,再交给l2 进行➗4,得到5.0 。
让lambda更具可读性-typealias
lambda函数类型的使用,是我们的代码更加繁琐,同时缺乏可读性,例如上面的 combine 函数,lambda中嵌套使用lambda,就会出现很多的函数类型的引用。针对这种情况,我们可以通过 类型别名 替代函数类型,使得代码更具有可读性。
类型别名允许为已经存在的类型取一个替代名,然后就可以在代码中使用了。关键字typealias
就用来定义别名,通过这样的定义,就可以简化我们的代码,我们来修改一下上面的combine函数。
首先我们定义类型别名:
typealias balabala = (Double)->Double复制代码
使用类型别名进行替换操作:
fun combine(lambda1:balabala, lambda2:balabala) : balabala{ return {x:Double -> lambda2(lambda1(x))} }复制代码
编译器看到 balabala 时,就会知道这就是一个 (Double)->Double
的占位符。
结束语
以上我们介绍了关于lambda的一些知识点,lambda的使用还是非常广泛的,它可以赋值给变量,可以是一种类型,可以当作函数的参数和返回值等等。
lambda是函数式编程的一个重要组成部分,非函数式编程读取输入数据并产生输出数据,而函数式编程可以将函数作为输入,并产生函数作为输出。理解lambda对于理解函数式编程会更有帮助。
作者:头顶的风
链接:https://juejin.cn/post/7025053548274515999