安卓语言基础之Kotlin高阶函数——Lambda表达式(一)
前段时间在技术交流群里看一群大佬在说Kotlin的高阶函数,看起来花里胡哨,对我这个以Java语言为生产工具的安卓开发十分不友好,但是为了能以后和他们一起更好的水群,我开始学习高阶函数,去了解它的魅力。
我将本篇归属于安卓开发基础必备系列,因为这部分知识属于开发语言基础知识,此外,高阶函数也是很多人推崇Kotlin的魅力所在。
#
首先我们看看Lambda编程,这个编程方式还是我在用Java开发安卓时候用点击事件时被Warnings提醒知晓的,AS推荐将匿名函数换为Lambda方式,再后面的学习中了解到这是一种语法糖。
语法糖(Syntactic sugar),也译为糖衣语法,是由英国科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。
Warning提醒我推荐替换成Lambda表达式:
vHello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
替换为:
vHello.setOnClickListener(v -> {
});
以上是Lambda表达式的一个例子,下面我们来详细看看这种编程方式:
集合的函数式API
我们通常去寻集合种元素长度最长的写法如下Demo:
val list = listOf("Apple", "Banana", "Pear")
var maxLengthFruit = ""
for (fruit in list) {
if (fruit.length > maxLengthFruit.length) {
maxLengthFruit = fruit
}
}
println("max length fruit is $maxLengthFruit")
而如果我们使用集合的函数式API就非常简洁:
val list = listOf("Apple", "Banana", "Pear")
val maxLengthFruit = list.maxBy { it.length }
println("max length fruit is $maxLengthFruit")
一段for循环直接简化为一句,但对于不熟悉这种表达方法的就会一头雾水,最早我接触的时候就感觉不利于阅读,其实学完后就感觉这种写法还是很爽的,接下来,为了看懂这种写法,我们先了解一下Lambda,它就是一小段可以作为参数传递的代码,通常情况下我们编程都是向某个函数传参时传入变量,但Lambda可以做到传入一小段代码,而且其实这一小段的定义中未规定长短(通常不建议在Lambda表达式编写过长的代码,防止影响代码可读性)。
Lambda表达式的语法结构如下:
{参数名1: 参数类型, 参数名2: 参数类型 -> 函数体}
我们可以看到,最外层是大括号,然后内部首先声明了参数列表,然后用“->”符号连接结尾的函数体,表示参数列表结束和函数体开始,函数体可以编写我们想实现的逻辑代码(不建议过长),最后位于最后一行的代码会自动作为Lambda表达式的返回值。
当然这是完整的写法,此外还有更为简化的版本,不过首先我们需要先了解Lambda的完整表达式,方便向更深层学习。
了解了完整的表达式,我们来一步步看如何简化到之前的代码的:
maxBy其实是一个普通的函数,它可以接受一个Lambda类型的参数,并会遍历集合时每次遍历的值作为参数传递给Lambda表达式,也就是可以根据我们传入的条件来遍历集合,然后找到该条件下的最大值。
知晓了maxBy函数,那么首先,我们可以根据完整表达式写出如下代码:
val list = listOf("Apple", "Banana", "Pear")
val lambda = { fruit: String -> fruit.length}
val maxLengthFruit = list.maxBy(lambda)
println("max length fruit is $maxLengthFruit")
我们将Lambda表达式作为参数传入了maxBy函数中,但其实是可以去简化该写法的,因为我们不需要专门去定义一个lambda变量,直接将其视为参数传入该函数中:
val list = listOf("Apple", "Banana", "Pear")
val maxLengthFruit = list.maxBy({ fruit: String -> fruit.length})
println("max length fruit is $maxLengthFruit")
此时,编译器会报Warning:
这告诉我们当Lambda参数是函数的唯一一个参数时,可以将函数的括号省略:
val list = listOf("Apple", "Banana", "Pear")
val maxLengthFruit = list.maxBy { fruit: String -> fruit.length }
println("max length fruit is $maxLengthFruit")
由于Kotlin有类型推导机制,所以我们的Lambda表达式的参数列表在大多数情况下其实不用声明参数类型,于是我们进一步将其简化:
val maxLengthFruit = list.maxBy { fruit -> fruit.length }
而Lambda表达式在参数列表中只有一个参数时甚至不用声明参数名,即可用it关键字来代替,于是最终变成我们最初看到的那个最简代码:
val maxLengthFruit = list.maxBy { it.length }
其他常用的集合函数式API
除了maxBy函数API以外,还有好几个很好用的集合函数式API,比如:
map函数
这个函数用于将集合的每个元素都映射成另外的值,映射规则在Lambda表达式中指定,然后生成一个新的集合,如下Demo:
val list = listOf("Apple", "Banana", "Pear")
val newList = list.map { it.uppercase(Locale.getDefault()) }
for (fruit in newList) {
println(fruit)
}
上述代码将list的元素全部变为大写,然后生成新的列表。
filter函数
这个函数可以用来过滤集合的一些数据,可以单独使用也能结合map函数一起使用,如下demo:
val newList = list.filter { it.length <= 5 }
.map { it.uppercase(Locale.getDefault()) }
上面代码可以将生成的新列表元素长度控制在5个字符以内。
any与all函数
any函数可以用于判断集合中是否至少存在一个元素满足指定条件,而all函数则用于判断集合中是否所有元素都满足指定条件,如下demo:
val anyResult = list.any { it.length <= 5 }
val allResult = list.all { it.length <= 5 }
println("anyResult is $anyResult, allResult is $allResult")
上面代码条件设置为元素长度是否在5个字符以内,allResult要所有元素满足该条件才能true,而anyResult只要有一个元素满足条件即返回true。