15.Lambda编程基础
下面我们先用一个例子慢慢引出lambda,从一个水果集里找到字符最长的水果,ef:
val list = listOf("Apple", "Banana", "Pear")
var maxLength = ""
for(fruit in list){
if(fruit.length > maxLength.length){
maxLength = fruit
}
}
println("max length is " + fruit)
但如果用集合的函数API,会更加简洁,ef:
val list = listOf("Apple", "Banana", "Pear")
var maxLength = list.maxBy{ it.length }
println("max length is "+ maxLength)
下面我们逐步来讲解一下为何可写得如此简洁,没错,会想到是lambda;lambda其实是 一小段 可作为参数传递的代码,完整语法结构为:
//函数体中可编写任意代码,但不建议太长,且函数体最后一行为lambda返回值
{ 参数名1:参数类型, 参数名2:参数类型 -> 函数体 }
前面的 函数API 语法结构似乎特殊,但其实maxBy就是个函数,只不过接收的是lambda,且它会在遍历集合时将每次遍历值作为参数传递给lambda,以此来找到最大值;了解后,下面我们套用lambda语法结构,将它传入maxBy中,ef:
val list = listOf("Apple", "Banana", "Pear")
val lambda = { fruit : String -> fruit.length }
val maxLength = list.maxBy(lambda)
但这显然啰嗦,可不需声明变量,直接将lambda传入maxBy中,ef:
val list = listOf("Apple", "Banana", "Pear")
val maxLength = list.maxBy({ fruit : String -> fruit.length })
由于kt中规定,当lambda为函数最后一个参数时,可将lambda移到函数括号外,ef:
val list = listOf("Apple", "Banana", "Pear")
val maxLength = list.maxBy() { fruit : String -> fruit.length }
由于lambda为函数唯一参数,所以括号也可省,ef:
val list = listOf("Apple", "Banana", "Pear")
val maxLength = list.maxBy { fruit : String -> fruit.length }
也由于kt的类型推导机制,lambda参数列大多情况可不必声明类型,简化得到,ef:
val list = listOf("Apple", "Banana", "Pear")
val maxLength = list.maxBy { fruit -> fruit.length }
最后,lambda参数列中只有一个参数,也不必声明参数名,用it关键字取代,最终得到,ef:
val list = listOf("Apple", "Banana", "Pear")
val maxLength = list.maxBy { it.length }
map函数式api,它用于将集合中的每个元素都映射成另外一个值,映射规则在lambda表达式中指定,最终生成新集合;下面将水果集的名变大写,ef:
fun main(){
val list = listOf("Apple", "Banana", "Pear")
val newList = list.map { it.toUpperCase() }
for(fruit in newList){
println(fruit)
}
}
//打印结果如下:
APPLE
BANANA
PEAR
filter函数式API,过滤集合数据,可单独使用或配合集合使用,比如只想保留字符长度为5内的水果并转成大写,ef:
fun main(){
val list = listOf("Apple", "Banana", "Pear")
//此处先调用filter再调用map,而不是先调用map再调用filter(因这样做会先映射集合
//所有元素再进行过滤,显然效率低了),先过滤后再进行集合内元素集合映射,效率提高
val newList = list.filter { it.length <= 5 }.map{ it.toUpperCase() }
for(fruit in newList){
println(fruit)
}
}
//打印结果如下
APPLE
PEAR
any和all 函数式API,any:判断集合内任意一元素满足条件,all:判断集合内所有元素满足条件,
ef:
fun main(){
val list = listOf("Apple", "Banana", "Pear")
val anyResult = list.any{ it.length <= 5 }
val allResult = list.all{ it.length <= 5}
println("anyFruit is " + anyFruit + ", allFruit is " + allFruit)
}
//打印如下
anyResult is true, allResult is false
以上是lambda语法结构和常用集合中的函数API,语法若掌握,其他函数API查阅文档即可掌握
java函数式API
上面我们已经学习了kt的函数式API,kt使用java方法也可进行函数式API,只不过有限制。如果kt调用了java函数,且该函数接收一个java单抽象方法接口(接口中只要一个抽象方法),ef:
public interface Runnable{
void run();
}
根据上面讲解,对于任何一个java方法,只要它接收Runnable参数,就可以调用函数式API。对此会有很多方法都接收Runnable,对此多为线程实现该Runnable,以Thread为例子,ef:
new Thread(new Runnable(){
@Override
public void run(){
System.out.println("running")
}
}).start();
注意这里用到了匿名内部类,下面我们来翻译成kt版本,ef:
Thread(object : Runnable {
override fun run() {
println("running")
}
}).start()
最终简化成如下,详细演化过程可回看我上文章中最后处关于object匿名内部类的实现,学习kotlin的一些笔录3_嗷喵喵没有吃鱼的博客-CSDN博客,ef:
Thread{
println("running")
}.start()
kt中会经常与java的调用,因为android sdk是用java编写的,下面再举例,android常用的点击接口,ef:
public interface OnClickListener{
void onClick(View v);
}
点击具体实现如下,ef:
bt.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
...
}
});
由于OnClickListener是java的单抽象方法接口,所以kt调用时可以用函数式API,对比上面的java调用则可以很简单,kt调用简化后如下,ef:
bt.setOnClickListener{
...
}