Kotlin笔记--Lambda表达式

      这一节学习Lambda 表达式,简称 lambda ,本质上就是可以传递给其他函数的一小段代码 。通过lambda,可以轻松地把通用的代码结构抽取成库函数, Kotlin 标准库就大量地使用了它们。 

  1 Lambda 简介

      作为函数参数的代码块,需要在代码中存储和传递一小段行为是常有的任务 。“当一个事件发生的时候运行这个事件处理器”又或者是“把这个操作应用到这个数据结构中所有的元素上”。在老版本的 Java 中,可以用匿名内部类来实现。这种技巧可以工作但是语法太唠唆了。函数式编程提供了另外一种解决问题的方法:把函数当作值来对待。可以直接传递函数,而不需要先声明一个类再传递这个类的实例 。假设你要定义一个点击按钮的行为,添加一个负责处理点击的监昕器。监听器实现了相应的接口 OnClickListener 和它的 一个方法onClick 。

/* Java */
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick (View view) {
/*点击后执行的动作*/
.,
}
});
button.setOnClickListener { //点击后执行的动作 }

说明:这段 Kotiin 代码和 Java 的匿名内部类做的事情一模一样,但是更简洁易读。

     2 Lambda 和集合

       良好编程风格的主要原则之一是避免代码中的任何重复,假设现在你有一个人的列表,需要找到列表中年龄最大的那个人。

fun findTheOldest(people: List<Person>)
var maxAge = O
var theOldest: Person? = null
for (person in people) {
if (person.age > maxAge) {
maxAge = person.age
theOldest = person
}
}
println(theOldest)
}
>> val people = listOf(Person(”Alice”, 29), Person("Bob”, 31))
> > findTheOldest(people)
Person(name=Bob, age=31)

分析:这里代码不少,而且很容易犯错。例如,你可能用了错误的比较,找到的是最小的元素而不是最大的那个 ,Kotlin 有更好的方法。可以使用库函数, 如下所示

>> val people= listOf(Person{”Alice”, 29) , Person(”Bob”, 31))
>> println (people. maxBy { it .age }) 
Person(name=Bob, age=31)

分析:maxBy 函数可以在任何集合上调用 ,且只需要 一个实参 : 一个函数 ,指定比较哪个值来找到最大元素。花括号中的代码{ it. age }就是实现了这个逻辑的lambda。 它接收一个集合中的元素作为实参(使用 it 引用它)并且返回用来比较的值 。

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))   //调用保存在变量中的lambda
3 

把 lambda 作为命名实参传递

>> val people= listOf(Person(”Alice”, 29), Person(”Bob”, 31))
>> val names = people.joinToString(separator z ””,transform= { p:Person -> p.name })
>> println(names)
Alice Bob

把 lambda 放在括号外传递

people.joinToString(" ") { p: Person -> p.name }

使用默认参数名称

people.maxBy { it.age }  // it 是自动生成的参数名称

仅在实参名称没有显式地指定时这个默认的名称才会生成。注意it 约定能大大缩短你的代码,但你不应该滥用它 。尤其是在嵌套
lambda 的情况下,最好显式地声明每个 lambda 的参数 。否则很难搞清楚 it 引用的到底是哪个值。如果上下文中参数的类型或意义都不是很明朗,显式声明参数的方法也很有效。如果你用变量存储 lambda , 那么就没有可以推断出参数类型的上下文,所以你必须显式地指定参数类型:

>> val getAge = { p: Person -> p.age }
>> people.maxBy(getAge)

4 在作用域中访问变量

       标准库函数 forEach 来展示,它所做的全部事情就是在集合中的每一个元素上都调用给定的 lambda。forEach 函数比普通 for 循环更简洁一些,下面的代码清单接收一个消息列表,把每条消息都加上相同的前缀打印出来。

fun printMessagesWithPrefix(messages: Collection<String>, prefix: String) {
messages.forEach {
    println (” $prefix $it”)
}
}

>> val errors= listOf (” 403 Forbidden",”404 Not Found”)
>> printMessagesWithPrefix(errors,”Error:”)
Error: 403 Forbidden
Error: 404 Not Found

这里 Kotiin 和 Java 的一个显著区别就是,在 Kotiin 中不会仅限于访问 final 变量,在 lambda 内部也可以修改这些变量;

5 成员引用

         如果你想要当作参数传递的代码己经被定义成了函数,该怎么办?当然可以传递一个调用这个函数的 lambda,但这样做有点多余。那么你能直接传递函数吗?Kotlin 和 Java 8 一样,如果把函数转换成一个值,你就可以传递它 。 使用::运算符来转换 :

val getAge = Person: : age

这种表达式称为成员引用, 它提供了简明语法,来创建一个调用单个方法或者访问单个属性的函数值。双冒号把类名称与你要引用的成员 (一个方法或者一个属性〉名称隔开,

val getAge = { person : Person -> person.age }

    注意,不管你引用的是函数还是属性,都不要在成员引用的名称后面加括号 。成员引用和调用该函数的 lambda 具有一样的类型,所以可以互换使用:

people. maxBy (Person: : age)

还可以引用顶层函数(不是类的成员):

fun salute() = println (” Salute ! ”)
>> run (::salute)
Salute!

分析:直接 以::开头。 成员 引用:: salute 被当作实参传递给库函数 run ,它会调用相应的函数。如果 lambda 要委托给一个接收多个参数的函数,提供成员 引用代替它将会非常方便:

val action = { person: Person, message: String - >
sendEmail (person, message)
}
val nextAction = :: sendEmail

可以用构造方法引用 存储或者延期执行创建类实例的动作 。 构造方法引用的形式是在双冒号后指定类名称:

data class Person(val name: String, val age: Int )
>> val createPerson = ::Person
>> val p = createPerson (”Alice” , 29)
>> println (p )
Person(name =Alice, age= 29)

注意,还可以用同样的方式引用扩展函数:

fun Person.isAdult() = age >= 21
val predicate = Person:: isAdult

尽管 isAdult 不是 Person 类的成员,还是可以通过引用访问它 , 这和访问实例的成员没什么两样 : person. isAdult ().

猜你喜欢

转载自blog.csdn.net/ljt2724960661/article/details/108434731
今日推荐