1.1、Lambda简介:作为函数参数的代码块
函数式编程:把函数当做值来对待。可以直接传递函数,而不是先声明一个类在传递这个类的实例。
1.2、Lambda和集合
data class Person(val name : String , val age : Int)
val people = listOf(Person("Alice",29),Person("Bob",31))
person.maxBy{it.age} //用Lambda在集合中搜索
//person.maxBy{Person :: age} //lambda刚好是函数或者属性的委托,可以用成员引用替换。
1.3、Lambda表达式的语法
Lambda表达式: {x:Int , y: Int -> x+y}
Kotlin的lambda表达式始终用花括号包围。实参并没有用括号括起来。箭头把实参列表和lambda的函数体隔开。
可以把lambda表达式存储在一个变量中,把这个变量当做普通的函数对待:
val sum = {x:Int , y: Int -> x+y}
println( sum(1,2) )
那么1.2中的例子我们可以改写为:
people.maxBy({ p: Person -> p.age })
Kotlin有这样一种语法约定,如果lambda表达式是函数调用的最后一个实参,他可以放到括号的外边。people.maxBy(){ p: Person -> p.age }当lambda是函数的唯一的实参时,还可以去掉调用代码中的空括号对:people.maxBy{ p: Person -> p.age }
省略lambda参数类型:people.maxBy{ p -> p.age } //推导出参数类型
对于不能推到出参数类型的情况,我们可以 先不声明类型,等编译器报错后在指定他们。可以指定部分实参数的类型,而剩下的实参只用名称。
终极简化 : 使用默认参数名称it代替命名参数。如果当前上下文期望的是只有一个参数的lambda且这个参数的类型可以推断出来,就会生成这个名称。
people.maxBy { it.age } //仅在实参名称没有显示地指定时这个默认的名称才会生成
1、不能滥用it,特别是在嵌套lambda的情况,最好显式的声明每个lambda的参数。
2、用变量存储lambda时,要显式地指定参数类型
val getAge = { p : Person -> p.age }
people.maxBy(getAge)
1.4、在作用域中访问变量
fun printlnProblemCounts(responses:Collection<String>){
var clientErrors =0
var serverErrors =0
responses.forEach {
if (it.startsWith("4")) {
clientErrors++
}else{
serverErrors++
}
}
}
和java比,在kotlin中不会仅限于访问final变量,Kotlin允许在lambda内部访问非final变量甚至修改他们。从lambda内访问外部变量,我们称这些变量被lambda捕捉。例如上面的clientErrors和serverErrors一样。默认情况下局部变量的生命周期别限制在声明的这个变量的函数中,但是如果他被捕捉了,使用这个变量的代码可以被存储并稍后在执行。当你捕捉final变量时,它的值和使用这个值的lambda代码一起存储;而对非final变量来说,它的值被封装在一个特殊的包装器中,这样你哦可以改变这个值,而对于这个包装器的引用会和lambda代码一起存储。
捕捉可变变量:实现细节
1、声明一个单元数的数组,其中存储可变值。
2、创建一个包装器的实例,其中存储要改变的值的引用。
1.5、成员引用
Kotlin和Java8一样,如果把函数转换成一个值,你就可以传递它。使用 :: 运算符来转换:
val getAge=Person::age //这种表达式成为成员引用
使用场景:
1、引用顶层函数。::函数名
2、lambda要委托给一个接收多个参数的函数,提供成员引用代替它。
3、构造方法引用 --- 在冒号后指定类名
4、引用扩展函数 。
网友评论