Lambda编程
一、Lambda表达式和成员引用
一)Lambda表达式语法
1 //注意与Java8中的区别 2 val sum={ x:Int,y:Int -> x+y } 3 println(sum(5,6)) 4 run { print(33) }
1 data class Person(val name:String,val age:Int) 2 3 fun main(args: Array<String>) { 4 val persons= listOf<Person>(Person("Tom",3),Person("Jerry",2)) 5 //println(persons.maxBy { it.age }) 6 7 //不使用任何简明语法的重写 8 //println(persons.maxBy({p:Person -> p.age})) 9 10 //如果lambda表达式是函数调用的最后一个实参,可以把它放到括号外面,但只能放一个 11 //println(persons.maxBy() { p:Person -> p.age }) 12 13 //lambda为唯一实参时,可以省略小括号 14 //println(persons.maxBy { p:Person -> p.age }) 15 16 //如果lambda的参数类型可以被推断出来,可以省略它 17 //println(persons.maxBy { p -> p.age }) 18 //使用原则:可以先不声明参数类型,观察编译器是否报错 19 20 //最简形式:使用默认参数名it代替参数名。如果当前上下文期望的 21 //是只有一个参数的的lambda且这个参数的类型可以推断出来。 22 println(persons.maxBy { it.age }) 23 }
1 fun main(args: Array<String>) { 2 //与Java不同,Kotlin允许在lambda内部访问非final变量,甚至修改它们 3 //默认情况下,局部变量的声明周期被限制在声明这个变量的函数中。但是如果 4 //它被lambda捕捉了,使用这个变量的代码可以被存储并稍后再执行。 5 6 fun tryToCountButtonClicks(button:Button):Int{ 7 var count=0 8 button.onClick {count++} 9 //这里会始终返回0 10 return count 11 } 12 }
成员引用:
//与Java8一样,如果把函数转换为一个值,你就可以传递它 var getAge=Person :: age //还可以引用顶层函数 run { ::tang } //构造方法引用存储或延期执行创建实例类的动作 val createPerson= ::Person val p=createPerson("Price",48) println(p) //引用扩展函数 fun Person.isAdult()= age>=21 val isAdult=Person::isAdult
二)集合的函数式API
1.filter函数:遍历集合并选出应用给定lambda后返回true的那些元素。
1 val list=listOf(1,2,3,4,5,6) 2 println(list.filter{it%2==0}) 3 /*[2, 4, 6]*/
2.map函数:对集合中的每一个运用给定函数并把结果收集到一个新集合。
val list= listOf(1,2,3)
println(list.map{it*it})
3.all函数:判断是否所有函数满足判定式
1 val list= listOf(1,2,3,4,5,6,7) 2 println(list.all{it>3})//false
4.any函数:检查是否至少存在一个匹配的元素
1 val list= listOf(1,2,3,4,5,6,7) 2 println(list.any { it>3 })//true
5.find函数:找到第一满足判定式的元素
1 val list= listOf(1,2,3,4,5,6,7) 2 println(list.find { it>3 })//4
6.groupBy函数:把列表转换为分组的map
1 val persons= listOf<Person>(Person("Tom",22), Person("Jimmy",22) 2 , Person("Jack",33),Person("Blank",33), Person("Price",50)) 3 println(persons.groupBy { it.age }) 4 /*{22=[Person(name=Tom, age=22), Person(name=Jimmy, age=22)], 5 33=[Person(name=Jack, age=33), Person(name=Blank, age=33)], 6 50=[Person(name=Price, age=50)]}*/
7.flatMap函数:处理嵌套集合中的元素
1 val list= listOf<String>("abc","de","fchi") 2 println(list)//[abc, de, fchi] 3 println(list.flatMap { it.toList() }) 4 /*[a, b, c, d, e, f, c, h, i]*/
8.flatten函数:只是平铺不做任何变换
9.惰性操作集合:序列
1 fun main(args: Array<String>) { 2 val persons= listOf<Person>(Person("Ajax",2), Person("Bob",6), 3 Person("Tom",5), Person("Auth",3)) 4 persons.map(Person::name).filter { it.startsWith("A") } 5 persons.map { p:Person ->p.name }.filter { it.startsWith("A") } 6 /* 7 * 上面的代码存在的问题:链式调用会创建两个列表,导致效率低下 8 * 此时可以使用序列 9 * */ 10 11 persons.asSequence() //把初始集合转换成序列 12 .map(Person::name) //序列支持和集合相同的API 13 .filter { it.startsWith("A") } 14 .toList() //转换回集合 15 16 /* 17 * 序列操作分为两类:中间和末端。中间操作返回的是另一个序列, 18 * 末端操作返回的是一个结果,这个结果可能是集合、元素、数字, 19 * 或者其他从初始集合变换序列中获取的任意对象。 20 * */ 21 //中间操作始终是惰性的 22 listOf(1,2,3,4).asSequence() 23 .map { println("map($it)"); it*it } //没有末端操作不会被执行 24 .filter { println("filter($it)") ; it%2==0} //没有末端操作不会被执行 25 }
注意:序列与Java8的Steam的区别:序列不支持在多个CPU上并行执行。
三)使用Java函数式接口
1 /*Java*/ 2 void post(int delay,Runnable com){}
TestIt().post(1000){ //注意整个程序只会创建一个Runnable实例 println("Run it") }
1 fun handle(id : String){ //lambda会捕捉id这个变量 2 TestIt().post(1000){ println("Run it $id")} //所以每次调用都会创建新的实例 3 }
SAM构造方法:显示地lambda转换为函数式接口
1 fun createAllDoneRunnable() :Runnable{ 2 //SAM构造方法名称和函数式接口名称一样,且只接收一个参数 3 return Runnable { println("All done") } 4 } 5 6 //SAM构造方法还可以把从lambda生成的函数接口实例,存储在变量中 7 val runIt= Runnable { println("Rock & Roll") }
四)带接收者的lambda:with与apply
1.with函数
1 fun alphabet() :String{ 2 val result=StringBuilder() 3 for (letter in ‘A‘..‘Z‘){ 4 result.append(letter) 5 } 6 result.append("\nDone") 7 return result.toString() 8 } 9 10 //用with函数简化 11 fun alphabe2() : String{ 12 val result=StringBuilder() 13 //with结构实际上是一个接收两个参数的函数,一个参数 14 //是result另一个是lambda表达式 15 return with(result){ 16 for (letter in ‘A‘..‘Z‘){ 17 this.append(letter) 18 } 19 //可省略this 20 append("\n Done2") 21 toString() 22 } 23 } 24 25 //进一步简化 26 fun alphabet3()= with(StringBuilder()){ 27 for (letter in ‘A‘..‘Z‘){ 28 append(letter) 29 } 30 append("\n Done3") 31 toString() 32 }
2.apply函数
几乎与with函数一样,与with函数的唯一区别是会返回作为实参传递给它的对象(接收者对象)