美文网首页
《Kotlin 实战》- 5 Lambda 编程

《Kotlin 实战》- 5 Lambda 编程

作者: Ronnie_火老师 | 来源:发表于2019-07-12 20:11 被阅读0次
  • lambda 本质是可以传递给其他函数的一小段代码。

5.1 Lambda 表达式和成员引用

  • 函数式编程与 lambda 表达式:函数式编程是把函数当做值来对待可以直接传递函数;lambda 表达式使得代码更简洁,不需要声明函数,可以高效地直接传递代码块作为函数参数。
button.setOnClickListener {/* 点击后的执行动作 */} // 注意是大括号
  • Lambda 语法:

    { x: Int, y: Int -> x + y}
    
    • 其中 -> 前的部分为参数,后面部分为函数体。注意到实参并没有用括号括起来,实参和函数体使用了 -> 符号隔开。
    • 可以把 lambda 表达式存储在一个变量中,这个变量当做普通函数对待
  • 看一下 lambda 表达式简写的演变过程:

// 前期准备
 val people = listOf(Person("Alice", 29), Person("Bob", 31))
 // maxBy 方法的系统声明
 fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? 
// 原始方式
people.maxBy({ p: Person -> p.age })
// lambda 表达式是函数调用的最后一个实参,可放到括号外面
people.maxBy(){ p: Person -> p.age }
// lambda 是函数唯一实参,可以去掉空括号对
people.maxBy { p: Person -> p.age }
// 与局部变量一样,如果 lambda 参数类型可以被推到,就可以省略类型
// 也存在不能被推到的情况,可以遵循这样的规则:先不声明类型,编译器报错后再声明
people.maxBy {p -> p.age}
// 当只有一个参数并且参数类型可以推导,就可以使用默认参数名称 it 代替命名参数
people.maxBy { it.age }
  • 当实参有多个 lambda 时,不能把超过一个 lambda 放到外面,所以都放在 () 倒是更好的选择

  • 当在函数内声明一个匿名 内部类的时候,能够在这个匿名类内部引这个函数的参数和局部变量。 也可以用 lambda 做同样的事情 。 如果在函数内部使用 lambda,也可以访问这个外部函数的参数以及在 lambda之前定义的局部变量。

    • 与 Java 不同的是,Kotlin 中不会仅限于访问 final 变量,在 lambda 内部也可以修改这些变量。
    • 从 lambda 内访问外部变量,称这些变量被 lambda 捕捉
  • 成员引用:Person:: age,类后(比如此处的 age)可以是函数也可以是属性,并且都无需加 ()。这样对于已经定义好的函数,也可以方便的作为参数传递。

5.2 集合的函数式API

  • 函数式编程风格在操作集合时提供了很多优势,kotlin 也加入了不少库函数来帮助解决集合问题。
  • filter :遍历集合并选出应用给定 lambda 后返回 true 的那些元素(即选出符合条件(lambda 函数)的集合元素,组成新集合
  • map:对集合中每个元素应用给定的 lambda 并把结果收集到一个新集合;
  • all:集合中是否所有元素都符合某条件(lambda),返回值是 boolean 类型;
  • any:集合中是否存在元素符合某条件,同样返回 Boolean 类型;
  • find:返回第一个符合条件的元素;
  • count:返回符合条件的元素总个数;
  • groupBy:把集合元素按照某特征(lambda)划分成不同的分组,返回是一个 map,key 为 lambda 中的条件,value 是列表集合中的元素;
  • flatMap:把结合中所有元素按照 lambda 做变化,然后想得到的结果“平铺”,返回平铺后的集合;
  • 注意:使用 lambda 表达式的代码看起来简单,有时候却掩盖了底层操作的复杂性,很容易写出不必要的重复计算的逻辑,尤其是对于集合的操作,产生不必要的循环或重复。所以始终要牢记你写的代码在干什么!

5.3 惰性集合操作:序列

  • 上面那些处理链表的函数,在链式调用时往往每一步都会创建新的链表,当处理大数据量时,效率较低。使用序列可以避免创建这些临时中间对象。

  • Kotlin 惰性集合操作的入口就是 Sequence 接口 。这个接口表示的就是一个可以逐个列举元素的元素序列。 Sequence 只提供了一 个方法 iterator,用来从 序列中获取值。Sequence 接口的强大之处在于其操作的实现方式 。序列中的元素求值是惰性的。因此,可以使用序列更高效地对集合元素执行链式操作,而不需要创建额外的集合来保存过程中产生的中间结果。可以调用扩展函数 asSequence 把任意集合转换成序列,调用 toList 来做反向转换。

  • listOf(1,2,3,4).asSequence().map{ it*it }.filter{ it>3 }.toList()
    
  • 这种操作其实主要分两步:中间操作和末端操作。中间操作始终都是惰性的,末端操作(toList() )触发执行了所有的延期计算。

  • 原理是“及早求值”,也就是会把序列中的元素,依次处理所有过程,这样有可能省去部分处理过程。比如我们把上例最后的 filter 变为 find,那么如果是对集合处理,会先所有元素求平方,再找第一个大于 3 的元素,而对于序列处理的话,会从第一个元素开始,先求平方,再看结果是否大于 3,如此找到第一个大于 3 的值就宣告结束了。

5.4 使用 Java 函数式接口

  • Kotlin 的 lambda 可以无缝地和 Java Api 互操作。

  • 把只含有一个方法的接口成为函数式接口,Android 中 OnClickListener,java 中比如 Runnable Callable 等都是函数式接口,Kotlin 允许你在调用接收函数式接口作为参数的方法时使用 lambda。

  • // java 中的声明
    public class View{
      public void setOnClickListener(OnClickListener l){...}
    }
    // Kotlin 调用
    button.setOnClickListener{...}
    
  • 参数可以有多个,只要含有函数式接口类型的参数,就可以使用 lambda

  • 实现原理:在 kotlin 中,每个 lambda 表达式都会被编译成一个匿名类,如果 lambda 捕捉了变量,每个被捕捉的变量会在匿名类中有对应的字段。

5.5 带接收者的 lambda:with 与 apply

  • With:可以用它对同一个对象执行多次操作,而无需反复把对象的名称写出来。

  • fun alphabet()= with(StringBuilder()){
      for (letter in 'A'..'Z'){
          append(letter)
      }
      toString()
    }
    
    • with 实际上是一个接收两个参数的函数,这个例子中两个参数是 StringBuilder 对象和一个 lambda,这里利用了把最后的 lambda 放到括号外的约定,这样看起来更像是内建的语言功能。并且示例代码中 lambda 内部省略了 this 引用。
  • apply:和 with 很像,但 apply 始终返回作为实参传递给它的对象。

  • fun alphabet()= StringBuilder().apply {
      for (letter in 'A'..'Z'){
          append(letter)
      }
    }.toString()
    
  • 二者区别:

    • 显然,with 是库函数,apply 是扩展函数
    • with 返回的是最后一行表达式(的值),apply 返回的是apply 其实是实参传进来的对象(接收者对象)。

5.6 小结

  • Lambda 允许你把代码块当作参数传递给函数。
  • Kotlin 可以把 lambda 放在括号外传递给函数,而且可以用 it 引用单个的 lambda 参数。
  • lambda 中的代码可以访问和修改包含这个 lambda 调用的函数中的变量。
  • 通过在函数名称前加上前缀 ::,可以创建方法、构造方法及属性的引用,并用这些引用代替 lambda传递给函数。
  • 使用像 filter、map、all、any 等函数,大多数公共的集合操作不需要手动迭代元素就可以完成。
  • 序列允许你合并一个集合上的多次操作 ,而不需要创建新的集合来保存中间结果。
  • 可以把 lambda 作为实参传给接收 Java 函数式接口(带单抽象方法的接口,也叫作 SAM 接口)作为形参的方法。
  • 带接收者的 lambda 是一种特殊的 lambda,可以在这种 lambda 中直接访问一个特殊接收者对象的方法。
  • with 标准库函数允许你调用同一个对象的多个方法,而不需要反复写出这个对象的引用 。apply 函数让你使用构建者风格的 API 创建和初始化任何对象。

相关文章

  • 《Kotlin 实战》- 5 Lambda 编程

    lambda 本质是可以传递给其他函数的一小段代码。 5.1 Lambda 表达式和成员引用 函数式编程与 lam...

  • kotlin lambda编程

    本文是对<>中 “lambda编程”一章的总结,主要记录了一些我认为比较重要的点 Lambda...

  • Kotlin学习笔记——lambda

    参考文献——kotlin之Lambda编程来自简书作者——程自舟 Kotlin lambda语法 完整写法 省略参...

  • Kotlin学习(5)Lambda编程

    定义 Lambda表达式,本质上就是可以传递其他函数的一小段代码。有了lambda,可以轻松的把通用的代码结构抽取...

  • Kotlin | 5.lambda 编程

    本章内容包括: Lambda 表达式和成员引用以函数式风格使用集合序列:惰性地执行集合操作在 Kotlin中使用 ...

  • Kotlin-Lambda编程-集合的创建与遍历

    Java到JDK1.8之后加入Lambda编程,Kotlin从第一个版本开始支持Lambda编程。 1、传统意义上...

  • Kotlin修炼指南(二):lambda表达式的精髓

    lambda表达式是Kotlin函数式编程的一个重要概念,要想掌握函数式编程,就必须熟练掌握lambda表达式,并...

  • android 书籍

    深入理解 Android 内核设计思想 实战Gradle kotlin实战 编程珠玑 编程之美 java核心技术卷...

  • 自定义函数式接口

    在 Kotlin 的 lambda 编程中有说过对于单个抽象方法的接口可以用 lambda 进行简写,这种特殊的接...

  • Kotlin:定义和目的

    《Kotlin实战》阅读笔记——Kotlin:定义和目的 Kotlin简介 一种针对Java平台的新编程语言。Ko...

网友评论

      本文标题:《Kotlin 实战》- 5 Lambda 编程

      本文链接:https://www.haomeiwen.com/subject/xutukctx.html