文章目录

  • 高阶函数
  • 内联函数
  • 几个有用的函数
  • 集合变换与序列
  • 集合的聚合操作
  • SAM 转换
  • 案例

高阶函数

​ 参数类型包含了函数,或者返回类型为函数

fun needsFunction(block: () -> Unit) {//调用函数block()
}fun returnsFunction(): () -> Long {//返回一个函数return { System.currentTimeMillis() }
}

内联函数

​ 当使用 Lambad 表达式时,他会被编译为一个匿名类。这表示每调用一次 lambad ,就会有一个额外的类被创建,这会带来运行的额外开销,导致 lambad 比使用一个直接执行相同代码的函数效率更低。

​ 如果使用 inline 标记一个函数,这个函数被调用的时候编译器并不会生成函数的调用代码。而是 使用函数实现的真实代码替换每一次函数的调用

​ 如下:

fun main() {cost {println("Hello")}
}// 添加 inline 即为内联函数
inline fun cost(block: () -> Unit) {val start = System.currentTimeMillis()block()println(System.currentTimeMillis() - start)
}

​ 反编译后如下

public static final void main() {int $i$f$cost = false;long start$iv = System.currentTimeMillis();int var3 = false;String var4 = "Hello";boolean var5 = false;System.out.println(var4);long var6 = System.currentTimeMillis() - start$iv;var5 = false;System.out.println(var6);}

​ Lambad 表达式和 cost 的实现部分都被内联了。看起来是调用了 cost ,实际上是函数本身被内联到了调用处。

​ 内联:

​ 1,函数本身被内联到调用处

​ 2,函数的函数参数被内联到调用处

内联函数的 return

val list = listOf<Int>(1, 2, 3, 4)list.forEach {if (it == 3) {//类似于 continue,退出本次return@forEach}println(it)}

non-local return

fun main() {//内联函数cost {println("Hello")return}println("not fond!")
}inline fun cost(block: () -> Unit) {val start = System.currentTimeMillis()block()println(System.currentTimeMillis() - start)
}
//Hello

如果在 lambad 中使用 return 关键字,他会从调用处返回,而并非是lambda 中返回。因为这是一个内联函数,表达式中的内容相当于被提取到了调用的地方进行执行,所以就会在 main 中直接 return。

从 lambda 中返回:

fun main() {//内联函数cost label@{println("Hello")return@label}println("not fond!")
}
//Hello
//1
//not fond!

增加 label 标签后,就可以局部返回。


//定义 内联函数 添加 inline 即为内联函数
inline fun cost(crossinline block: () -> Unit): Runnable {return object : Runnable {override fun run() {block() //报错,因为 block 的调用处与定义处不在同一个调用上下文//添加 crossinline 关键字即可解决 ,禁止 non-local return//添加 noinlin 也可以解决,只不过 内联函数就没有意义了。}}
}

内联属性

var pocket: Double = 0.0
var money: Doubleinline get() {return pocket}inline set(value) {pocket = value}

和 内联函数一样,在调用 money 的时候会执行在调用的地方

内联函数的限制

1,public /protected 的内联方法只能访问对应类的 public 成员

2,内联函数的内联函数参数不能存储(赋值给变量)

3,内联函数的内联函数参数只能传递给其他内联函数参数

几个有用的函数

函数名 介绍 推荐指数
let val r = X.let{x -> R} ★★★
run val r = X.run{this.X ->R}
also val x = X.also{x -> Unit} ★★★
apply val x = X.apply{this:X -> Unit}
use val r = Closeable.use{c -> R} ★★★

​ let / run :r = lambda 表达式的返回值

​ alse / apply : x = receiver ,也就是当前的对象

​ use :有些需要关闭的代码,如流,等可以在 Closeable 中执行,执行完后会自动关闭

class Person(var name: String, var age: Int)fun main() {val person = Person("benny", 20)var y = person.let(::println)var x = person.run(::println)person.also {it.name = "张三"}person.apply {name = "李四"}File("build.gradle").inputStream().reader().buffered().use {//内部做了异常处理,流关闭。点击 use 查看源码println(it.readLine())}
}

集合变换与序列

集合的映射操作

函数名 说明
filter 保留满足的元素
map 集合中的所有元素 — 映射到其他元素构成新的集合
flatMap 集合中的所有元素 — 映射到新的集合并合并这些集合得到新的集合
fun main() {val list = listOf<Int>(1, 2, 3, 4, 5)//    集合的映射操作
//    filterlist.filter { it % 2 == 0 }   //2,4list.asSequence().filter { it % 2 == 0 }//2,4//     mapval m1 = list.map { it * 2 + 1 }        // 3,5,7,9,11val m2 = list.asSequence().map { it * 2 + 1 } // 3,5,7,9,11//        flatmap
//      将一个元素映射成一个集合,再把所有的集合全部拼成一个新的集合val f1 = list.flatMap {//返回一个范围,将序列映射成一个集合0 until it}// 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4}

Kt 中的懒汉序列

fun main() {val list = listOf<Int>(1, 2, 3, 4, 5)//懒序列 asSequencelist.asSequence().filter {println("filter: $it")it % 2 == 0}.map {println("map: $it")it * 2 + 1}.forEach {println("forEach: $it")}
}
//结果
filter: 1
filter: 2
map: 2
forEach: 5
filter: 3
filter: 4
map: 4
forEach: 9
filter: 5

​ 懒序列是将集合中的元素一个个的往下执行,首先是 1,it % 2 条件不满足,接着是 2,满足,然后才会到 map 中打印出来接着变成5,然后到 forEach 中打印5。2 完了之后就是3,又从 filter 开始。一直到最后一个元素。

​ **注意:**懒汉序列中,如果不写 forEach ,上面的 filter 和 map 都不会执行!

Kt 中恶汉序列

 list.filter {println("filter: $it")it % 2 == 0}.map {println("map: $it")it * 2 + 1}.forEach {println("forEach: $it")}
//结果
filter: 1
filter: 2
filter: 3
filter: 4
filter: 5
map: 2
map: 4
forEach: 5
forEach: 9

​ 结果很明显

集合的聚合操作

函数名 说明
sum 所有元素求和
reduce 将元素依次按规矩聚合,结果与元素类型一致
fold 给定初始化值,将元素按规矩聚合,结果与初始化类型一致

fold

 //    foldval f = list.fold(String()) { s: String, i: Int ->s + i}println(f)   //12345

​ 上面是执行过程

​ 会有一个初始值,就是上面的 s ,然后和每一个元素进行拼接,最终将 s 返回。

reduce

​ 少了初始值

 val r = list.reduce() { acc, i ->acc + i}println(r)    //15

​ 可以看到,少了一个参数,不能指定类型,这里 acc 是 Int 类型

sum

val s = list.sum()

zip

fun main() {val list = listOf<Int>(1, 2, 3, 4, 5)val array = arrayOf(2, 2)val z = list.zip(array) { a: Int, b: Int ->a * b}z.forEach {println(it)}   //2,4
}

​ 结果是 2,4。为啥呢?看一哈源码就明白了:

public inline fun <T, R, V> Iterable<T>.zip(other: Array<out R>, transform: (a: T, b: R) -> V): List<V> {//拿到数组的长度val arraySize = other.size//创建一个新的集合val list = ArrayList<V>(minOf(collectionSizeOrDefault(10), arraySize))var i = 0//遍历集合,也就是调用这个方法的集合for (element in this) {//i 必须 小于数组的长度if (i >= arraySize) break//这里调用了我们传入的函数,传入当前元素,和 数组中的元素list.add(transform(element, other[i++]))}//返回一个集合return list
}

​ 是不是非常清晰呢?

SAM 转换

SAM: Single Abstract Method ,一个抽象方法

JAVA:一个参数类型为 只有一个方法的接口的方法调用时可以使用 Lambda 表达式做转换参数

Kotlin:一个参数类型为 只有一个方法的 Java 接口的 Java 方法 ,调用时可用 Lambda 表达式作为转换参数。

例如:

一个 Java 的接口 和 类

interface Runnable {void run(int x);
}public class Main {public void execute(Runnable runnable) {}
}

在 java 中调用可以使用 Lambda 做转换

在 Kt 中调用如下

main.execute { x ->}

在 Kt 中调用 execute,可以传入一个 Lambda,而不需要 Runnable 的实例,因为在 Kt 看来,这个方法本身是可以接受两种类型的:Runnable 和 (x:Int)->Unit,前面任何一个条件不成立,这个结果就不成立,例如,Runable 是一个 Kotlin 类,或者 execute 是一个 Kotlin 方法都不可以

如果在 Kt 中定义了一个接口和 函数,应该怎么传递呢:

fun setR(r: R) {}interface R {fun run()
}fun main(){//object :R 表示创建一个匿名内部类setR(object :R{override fun run() {}})
}

​ 通过上面这种方法即可。

Java 的 Lambda 是假的,本质上就是 SAM 转换。

Kotlin 的 Lambda 是真的,只是支持了 SAM 转换,所以在调用 java 方法的时候可以 使用 Lambda。

SAM 转换的坑

看例子:

// JAVA
public class Main {interface Runnable {void run(int x);}List<Runnable> list = new ArrayList<>();public void add(Runnable runnable) {list.add(runnable);}public void remove(Runnable runnable) {list.remove(runnable);}public int size() {return list.size();}
}
fun main() {val main = Main()//添加 Runnable// 使用 SAM 转换,其实就是创建了一个 匿名内部类main.add {println(it)}//上面这种就是如下的形式//    main.add(object :Main.Runnable{//        override fun run(x: Int) {//            {//                println(x)
//            }()
//        }
//    })println(main.size())//删除main.remove {println(it)}println(main.size())
}

结果:1,1。没有删除掉,因为 Remove 删除的是一个新的 Lambda。不可能删除

也有一些人发现了这个问题,采用如下解决方法:

fun main() {val main = Main()val r = { i: Int ->println(i)}main.add(r)println(main.size())main.remove(r)println(main.size())
}

但是这种真的有用吗?

结果还是 :1,1

其实最终调用还是如下:

//还是一个匿名内部类
main.add(object : Main.Runnable {override fun run(x: Int) {r.invoke(x)}
})

解决

 val r = object : Main.Runnable {override fun run(x: Int) {println(x)}}

​ 直接定义一个匿名内部类,然后添加删除即可 。


案例

**练习1:**找出一个文件中每个字符出现多少次

fun main() {File("build.gradle").readText() //读文件.toCharArray()//得到字符数组.filterNot(Char::isWhitespace) //过滤空白.groupBy { it } //按照 it 分组,分组后 key 是 char,值是对应的 list// map,将元素映射为Pair.map {it.key to it.value.size}//打印.let {println(it)}
}

练习2:HTML DSL

interface Node {fun render(): String
}class StringNode(val content: String) : Node {override fun render(): String {return content}}class BlockNode(val name: String) : Node {val children = ArrayList<Node>()val properties = HashMap<String, Any>()override fun render(): String {return """<$name${properties.map { "${it.key}='${it.value}'" }.joinToString(" ")}>${children.joinToString("") { it.render() }}</$name>"""}operator fun String.invoke(block: BlockNode.() -> Unit): BlockNode {val node = BlockNode(this)node.block()this@BlockNode.children += nodereturn node}operator fun String.invoke(value: Any) {this@BlockNode.properties[this] = value}operator fun String.unaryPlus() {this@BlockNode.children += StringNode(this)}
}/*** 接收一个 BlockNode 的扩展函数* 如果传入 Lambda,这个 Lambda 中就可以调用 BlockNode 中的成员函数了*/
fun html(block: BlockNode.() -> Unit): BlockNode {val html = BlockNode("html")//调用传入的 lambdahtml.block()return html
}/*** 扩展函数*/
fun BlockNode.head(block: BlockNode.() -> Unit): BlockNode {val head = BlockNode("head")head.block()this.children += headreturn head
}/*** 扩展函数*/
fun BlockNode.body(block: BlockNode.() -> Unit): BlockNode {val body = BlockNode("body")body.block()this.children += bodyreturn body
}fun main() {val htmlContent = html {head {//重载String的 invoke 运算符,接收一个 Lambda"meta"{//重载String 的 invoke 运算符,接收一个 Any"charset"("UTF-8")}}body {"div" {"style"("""width: 200px; height: 200px; line-height: 200px; background-color: #C9394A;text-align: center""".trimIndent())"span" {"style"("""color: white;font-family: Microsoft YaHei""".trimIndent())//重载 String 的 + 运算符+"Hello HTML DSL!!"}}}}.render()File("Kotlin.html").writeText(htmlContent)
}

​ 其实就是遍历。在 main 方法中 调用 html 方法,传入一个 lambda。在 html 方法中执行这个lambda。然后就是执行 head 方法,head 中 先执行 head 里面的方法,一直递归执行,知道执行到最后一个后才会 this.children += head。然后由内到外执行。执行完后就到了 body。也是同样的道理,递归遍历,children 集合中的数据排列顺序都是从 html 的内部一直到外部。

​ 最终调用功能 render 函数,从外向内的递归遍历出来。


  • 高阶函数

    • 概念:参数类型包含了函数,或者返回值类型为函数。函数可被传递
    • 常见的高阶函数:forEach,map
    • 高阶函数的调用
  • 内联函数

    • 内联的概念:将函数放在调用处执行,提高了性能
    • 内联函数的写法: inline
    • 高阶函数的内联
      • return,non-local-return
    • 内联属性
  • 几个有用的函数

    • 按返回值的结果

      • let,run
    • 返回 Receiver
      • also ,apply
    • 自动关闭资源
      • use
  • 集合变换与序列

    • 集合映射

      • filter,map,flatMap
    • 集合聚合

      • fold ,reduce,sum,zip
    • 懒序列的机制

  • SAM

    • 匿名内部类
    • SAM 概念:一个抽象方法
  • 高阶函数

    • 概念:参数类型包含了函数,或者返回值类型为函数。函数可被传递
    • 常见的高阶函数:forEach,map
    • 高阶函数的调用
  • 内联函数

    • 内联的概念:将函数放在调用处执行,提高了性能
    • 内联函数的写法: inline
    • 高阶函数的内联
      • return,non-local-return
    • 内联属性
  • 几个有用的函数

    • 按返回值的结果

      • let,run
    • 返回 Receiver
      • also ,apply
    • 自动关闭资源
      • use
  • 集合变换与序列

    • 集合映射

      • filter,map,flatMap
    • 集合聚合

      • fold ,reduce,sum,zip
    • 懒序列的机制

  • SAM

    • 匿名内部类
    • SAM 概念:一个抽象方法
    • Lambda:JAVA 中本质上就是 SAM 。Kt 中的 Lambda 就是匿名函数

参考自慕课网 Kotlin 从入门到精通

kotlin 之函数进阶相关推荐

  1. python 函数进阶_Python学习入门基础:一篇文章搞定函数基础、函数进阶

    一.函数基础函数的快速体验 函数的基本使用 函数的参数 函数的返回值 函数的嵌套调用 在模块中定义函数私信小编001即可获取Python学习资料01. 函数的快速体验 1.1 快速体验 所谓函数,就是 ...

  2. 好好学python·函数进阶(递归函数,回调函数,闭包函数,匿名函数,迭代器)

    函数进阶 递归函数 回调函数 闭包函数 特点 匿名函数 lambda 表达式 迭代器 iter() next() 迭代器的取值方案 迭代器取值特点,取一个少一个,直到都取完,最后再获取就会报错 检测迭 ...

  3. 学习Kotlin(五)函数与Lambda表达式

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  4. 41、Power Query-Text.Combine函数进阶2

    本节继续讲解Power Query-Text.Combine函数进阶. 下面看一个更加有趣的例子. 比如上图,有多列,我们需要求出唯一值,标准有两个,分别是以左边为基准和以右边为基准. 比如以左边为基 ...

  5. 深入理解javascript函数进阶系列第一篇——高阶函数

    前面的话 前面的函数系列中介绍了函数的基础用法.从本文开始,将介绍javascript函数进阶系列,本文将详细介绍高阶函数 定义 高阶函数(higher-order function)指操作函数的函数 ...

  6. Kotlin之函数作为参数传递

    1 .Kotlin之函数作为参数传递 我们在写BaseQuickAdapter适配器的时候,有时候嵌套多个BaseQuickAdapter,如果最里面的view触发点击事件,我们可以把函数作为参数通过 ...

  7. python函数-函数进阶

    python函数-函数进阶 一.命名空间和作用域 1.命名空间 内置命名空间 -- python解释器 就是python解释器一启动就可以使用的名字存储在内置命名空间中 内置的名字在启动解释器的时候被 ...

  8. function函数嵌套 matlab_Matlab函数进阶:使用匿名函数和内嵌函数处理多变量传递问题...

    Matlab 函数进阶: 使用匿名函数 (Anonymous Function) 和内嵌函数 (Nested Function) 处理多变量传递问题 (Matlab 7.0 以上 ) 问题: 有一个多 ...

  9. Python学习入门基础:一篇文章搞定函数基础、函数进阶

    一.函数基础 函数的快速体验 函数的基本使用 函数的参数 函数的返回值 函数的嵌套调用 在模块中定义函数 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在 ...

最新文章

  1. algorithm头文件函数全集——史上最全,最贴心
  2. java学习(103):字符串概述
  3. 博文视点大讲堂36期——让Oracle跑得更快 成功举办
  4. php html页面获取session,怎么在html中获取session变量
  5. 上海电力学院计算机组成与结构试卷,上海电力学院试卷及成绩管理办法
  6. 外籍专家在中关村图书大厦解密软件项目
  7. 阿克曼函数求解(递归和非递归)
  8. python数字转拼音输出,[python] pinyin 模块 -- 将汉字文本转化为拼音
  9. 自主研发的流程引擎怎么样?好用吗?
  10. java docx4j 合并word_如何使用docx4j在word中添加合并字段?
  11. 奥比中光深度摄像头_奥比中光展示智能深度3D摄像头技术解决方案
  12. 安卓(Android)手机如何安装APK?
  13. Linux 下的Chm 文件阅读器
  14. layui获取选中行数据
  15. java把URL转换成二维码并保存在指定的位置
  16. 量子计算 19 量子算法4 (Shor Part I)
  17. [股市]散户高手的炒股心得(收藏)
  18. adb 命令获取安卓设备IMEI码
  19. python开源web项目-最火的五大 python 开源项目
  20. opencv 图像梯度(python)

热门文章

  1. C语言复习——嵌入式相关
  2. 边界函数Bounding Function(成长函数的上界)
  3. 项目中常用的github库集合
  4. 使用fontawesome字体
  5. undefined symbol: _ZN6caffe26detail36_typeMetaDataInstance_preallocated_7E解决办法
  6. [目标检测]CenterNet
  7. Java项目:springboot酒店宾馆管理系统
  8. msxml document class EOleSysError with message '没有注册类别' 错误的解决
  9. 2021年电气试验新版试题及电气试验模拟试题
  10. 用云来轻APP,长江商学院EE论坛这么做