本文是对<<Kotlin in Action>>
的学习笔记,若是须要运行相应的代码能够访问在线环境 try.kotlinlang.org,这部分的思惟导图为: java
Lambda
表达式,本质上是能够 传递给函数的一小段代码,能够轻松地把通用的代码结构抽取成库函数,Kotlin
标准库就大量地使用了它们。数组
Lambda
的应用场景有:bash
在Java
中,能够用匿名内部类来实现,可是它的语法很啰嗦,下面咱们演示用Lambda
来实现点击监听:数据结构
button.setOnClickListener { /* 点击后执行的动做 */}
复制代码
咱们对集合执行的大部分任务都遵循几个通用的模式,因此实现这几个模式的代码应该放在一个库里,下面咱们演示一个例子:将Person
数据类放到一个集合当中,并从中选出年龄最大的一我的。 函数
maxBy
函数,它只须要一个实参:
一个函数,指定比较哪一个值来找到最大的元素,花括号中的代码
{ it.age }
就是实现了这个逻辑的
lambda
,它接收一个集合中的元素做为实参(使用
it
引用它)而且返回用来比较的值,在上面的例子中:
Person
对象age
属性中的值若是lambda
恰好是 函数或者属性的委托,能够用 成员引用 替换。学习
people.maxBy(Person :: age)
复制代码
一个Lambda
表达式把一小段行为进行编码,你能把它 当作值处处传递,它能够被 独立地声明并存储到一个变量中,可是最多见的仍是直接声明它并传递给函数,下面是一个Lambda
表达式的语法,->
前为 参数,后为 函数体,始终用 花括号包围。编码
{x : Int, y : Int -> x + y}
复制代码
能够将Lambda
表达式存储在一个变量中,把这个变量当作普通函数对待(即经过相应的实参调用它): spa
若是须要把一小段代码封闭在一个代码块中,可使用库函数run
,这种调用和内建语言结构同样高效且不会带来额外运行时开销: 3d
如今,让咱们回到最开始寻找集合中年龄最大的人的例子,它本来的调用方法以下,maxBy
函数接收一个lambda
表达式{ p : Person -> p.age }
做为参数:code
people.maxBy({ p : Person -> p.age })
复制代码
上面这段代码的解释为:花括号中的代码片断是lambda
表达式,把它做为实参传给函数,这个lambda
接收一个类型为Person
的参数并返回它的年龄。下面,咱们一块儿来看一下如何简化这个表达式:
Kotlin
有一个语法规定,若是lambda
表达式是函数调用的 最后一个实参,它能够 放到括号的外边,所以上面的例子简化为://第一步:将 lambda 表达式放到括号的外边。
people.maxBy() { p : Person -> p.age }
复制代码
lambda
是函数 惟一的实参,还能够 去掉调用代码中的空括号对//第二步:去掉空括号对。
people.maxBy { p : Person -> p.age }
复制代码
lambda
参数的类型能够被推倒出来,你就不须要显示地指定它,以maxBy
函数为例,其 参数类型始终和集合的元素类型相同,所以编译器知道你是对Person
对象的集合调用maxBy
函数,能够简化为://第三步:省略 lambda 参数类型。
people.maxBy { p -> p.age }
复制代码
可是若是咱们用变量存储lambda
,那么就没有能够推断出参数类型的上下文,因此你必须显示地指定参数类型:
//没法推断出参数的类型,必须显示地指定参数的类型。
val getAge = { p : Person : p.age }
people.maxBy (getAge)
复制代码
it
代替命名参数://第四步:使用默认参数名称。
people.maxBy { it.age }
复制代码
在 Kotlin 知识梳理(2) - 函数的定义与调用 中,咱们经过joinToString
介绍了命名参数和默认参数值的用法,实际上在标准库中也有定义这个函数,不一样之处在于它能够接收一个附加的函数参数,这个函数能够用toString
函数之外的方法来把一个 元素转换成字符串,下面显示如何 只打印出人的名字
lambda
表达式能够包含更多的语句,最后一个表达式就是lambda
的结果:
当在函数内声明一个匿名内部类时,可以在这个匿名内部类引用这个函数的参数和局部变量,也能够用lambda
作一样的事情,若是在函数内部使用lambda
,也能够访问这个函数的参数,还有在lambda
以前定义的局部变量。
下面咱们用标准库函数forEach
来展现这种行为,它是最基本的集合操做函数之一:它所作的所有事情就是在集合中的每个元素之上都调用给定的lambda
:
Kotlin
中不会仅限于访问final
变量,在lambda
内部也能够修改这些变量,下面的代码中对给定的相应状态码set
分别进行计数。
Kotlin
中,它容许在
lambda
内部访问非
final
变量甚至修改它们。从
lambda
内访问外部变量,咱们称这些
变量被 lambda 捕捉,就像上面例子中的
clientErrors
和
serverErrors
。
默认状况下,局部变量的生命周期被限制在声明这个局部变量的函数当中,可是若是它被lambda
捕捉了,使用这个变量的代码能够被存储并稍后执行,原理为:
final
变量时,它的值和使用这个值的lambda
代码一块儿存储。final
变量,它的值被封装在一个包装器中,这样你就能够改变这个值,而对这个包装器的引用会和lambda
代码一块儿存储。Java
只容许捕捉final
变量,而当你想捕捉可变变量的时候,可使用两种技巧:
这样,当捕捉了一个可变变量var
的时候,它的值被做为Ref
类的一个实例被存储下来,Ref
变量是final
的能轻易被捕捉,而后实际值存储在其字段中,而且能够在lambda
内被修改。
在上面的例子中,咱们演示了 如何让你把代码块做为参数传递给函数,可是若是要当作参数传递的代码已经被定义成了函数,这时候就须要 把函数转换成一个值,这种方式称为 成员引用。
val getAge = Person :: age
复制代码
它提供了简明的语法,来建立一个 调用单个方法或者访问单个属性的函数值,双冒号把 类名称 与 你要引用的成员(一个方法或者属性)名称 隔开。
成员引用和调用该函数的lambda
具备同样的类型,因此能够互换使用:
people.maxBy(Person : age)
复制代码
除此以外,还能够引用顶层函数,这里咱们省略了类名称,直接以:
开头,成员引用::salute
被看成实参传递给库函数run
,它会调用相应的函数:
咱们还可使用 构造方法引用 存储或者延期执行建立类实例的动做,构造方法引用的形式是 在双冒号后指定类的名称:
isAdult
方法,选出年龄大于21
的人。