Lambda表达式和成员引用

2018-07-19  本文已影响0人  Ryan_Hoo

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、引用扩展函数 。

上一篇 下一篇

猜你喜欢

热点阅读