文章目录

  • 高阶函数
    • 定义高阶函数
      • 函数类型
      • 高阶函数示例
    • 内联函数
      • 内联函数的作用
      • 内联函数的用法
    • noinline与crossinline

高阶函数

定义高阶函数

  • 高阶函数和Lambda的关系是密不可分的.
  • 像接受Lambda参数的函数就可以称为具有函数式编程风格的API了
  • 当我们想要定义自己的函数式API那就得借助高阶函数来进行实现了.
  • 高阶函数的定义:如果一个函数是另外一个函数,或者一个函数的返回值是另外一个函数,那么就称这个函数为高阶函数.

函数类型

  • 在编程语言当中由整形,布尔类型等字段类型,而Kotlin又增加了一个函数类型的概念

  • 如果我们将这个函数类型添加到一个函数的参数声明或者返回值声明当中,那么这就是高阶函数

  • 定义一个函数类型,不同于定义一个普通的字段类型,函数类型的语法规则如下:

  • (String, Int) -> Unit

  • 既然是定义一个函数类型,那么最关键的就是要声明该函数的参数以及它的返回值是什么.

  • 因此,->左边部分就是用来声明该函数接收什么参数,多个参数之间使用逗号进行隔开,如果不接受任何参使用一对空括号即可.

  • 如果没有返回值就使用Unit,它大致相当于java当中的void

  • 现在将上述函数类型添加到一个函数的参数当中,那这个函数就是一个高阶函数

fun example(func: (String, Int) -> Unit) {func("hello", 123)
}
  • 在example()函数当中接收了一个函数类型的参数,因此example()函数就是一个高阶函数
  • 而调用一个函数类型的参数,语法类似与调用一个普通的函数,只需要在参数名后面加上一对括号,并在括号当中传入必要的参数
  • 高阶函数允许让函数类型的参数来决定函数的执行逻辑,即使是同一个高阶函数,只要出传入不同的函数类型参数,那么它的执行逻辑和最终返回结果就可能是完全不同的

高阶函数示例

  • 新建一个HigherOrderFunction.kt文件
  • 在其中编写一个高阶函数
fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int) : Int {val result = operation(num1, num2)return result
}
  • 高阶函数定义好了,该高阶函数接收了一个函数类型的参数,因此我们要去编写所对应的函数类型相匹配的函数即可.
fun plus(num1: Int, num2: Int) : Int {return num1 + num2
}fun minus(num1: Int, num2: Int) : Int {return num1 - num2
}
  • 这里定义了两个函数,都是和高阶函数中函数类型的参数相匹配的,但是这两个函数却实现的是不同的功能,第一个是俩数之和,第二个是俩数之差.
  • 有了上述的函数之后,我们就可以调用num1AndNum2()函数了,在main函数当中编写如下代码
fun main() {val num1 = 5val num2 = 1val res1 = num1AndNum2(num1, num2, ::plus)val res2 = num1AndNum2(num1, num2, ::minus)println("res1 is $res1")println("res2 is $res2")
}
  • 这里需要注意的就是调用num1AndNum2()函数的方式,第三个参数使用使用了::plus和::minus的写法,这是一种函数引用的写法,表示将plus()和minus()函数作为参数传递给num1AndNum2()函数
  • 其实我们还可以不编写plus()函数和minus()函数,而是使用Lamdba表达式的方式来调用高阶函数
fun main() {val num1 = 5val num2 = 1val res1 = num1AndNum2(num1, num2) { num1, num2 ->num1 + num2}val res2 = num1AndNum2(num1, num2) { num1, num2 ->num1 - num2}println("res1 is $res1")println("res2 is $res2")
}
  • 使用高阶函数模拟一个apply函数的类似功能
  • 给StringBuilder类定义一个扩展函数builder(),然后这个扩展函数builder又接收了一个函数类型的参数,这个函数类型的参数它的参数为空,返回值也为空,但是builder扩展函数的返回值为StringBuild
fun StringBuilder.builder(block: StringBuilder.() -> Unit) : StringBuilder {block()return this
}
  • 需要注意的就是这个函数类型的声明方式和之前的示例有所不同,它在函数类型参数前面加上了StringBuilder.,这个意思就是在函数类型之前加上ClassName.表示该函数类型的定义在哪个类当中,这个才是定义高阶函数完整的语法规则.
  • 现在我们就可以使用builder函数来代替apply函数完成一些操作
fun main() {val list = listOf("Apple", "Banana", "Orange")val res = StringBuilder().builder {append("Start eating fruits.\n")for (f in list) {append(f).append("\n")}append("Ate all fruits.")}println(res)
}

内联函数

内联函数的作用

  • 要分析内联函数的作用,得先知道高阶函数的原理
  • 我们首先要知道Kotlin中的代码最后还是要转换称为Java字节码的
  • 但是Java当中又没有对应的高阶函数的概念,其实这一切都要归功于Kotlin的编译器,Kotlin的编译器会将高阶函数的代码转换称为Java当中的语法结构.
  • 转换称为Java语法结构之后,函数类型的参数会被转换成为一个接口,这个接口是Kotlin内置的一个接口,在这个接口当中有一个带实现的invoke()方法
  • 所以最后实际上调用函数类型参数就是调用了接口的invoke()函数,并将相应的参数传递进去即可.
  • 然后之前函数类型参数的Lambda表达式就会改写成为了接口的匿名类实现
  • 原来我们一直使用的Lambda表达式会在底层被转换成为匿名类的实现方式
  • 这就表明我们每调用一次Lambda表达式,都会创建一个新的匿名类实例,这也会造成额外的内存和性能开销
  • 为了解决这个问题,Kotlin提供了内联函数的功能,它可以将Lambda表达式带来的内存和性能开销问题完全消除.

内联函数的用法

  • 内联函数的用法非常简单只需要在定义高阶函数时加上inline关键字的声明即可
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {val result = operation(num1, num2)return result
}
  • 那么内联函数的工作原理其实就是:Kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样也就不存在运行时的开销了.
  • 大致过程就是:Kotlin编译器会将Lambda表达式中的代码替换到函数类型参数调用的地方

  • 然后再将内联函数中的代码替换到函数调用的地方

  • 最终的代码就成了这个样子

  • 这样内联函数就可以完全消除Lambda表达式带来的内存和性能开销问题

noinline与crossinline

  • 一个比较特殊的情况.比如,一个高阶函数中如果接收了两个或者更多函数类型的参数,这时我们给函数加上了inline关键字,那么Kotlin编译器会自动将所有引用的Lambda表达式全部进行替换.
  • 但是我们现在只想给其中一个Lambad表达式该怎么办,那么就可以使用noline关键字,如下所示:
inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit) {}
  • 为什么Kotlin要提供一个noinline关键字来排除内联功能?因为内联函数类型参数在编译的时候会被进行代码替换,因此他没有真正的参数属性,非内联的函数类型参数可以自由地传递给其他任何函数,因为它就是一个真实参数,而内联函数类型参数只允许传递给另外一个内联函数,这就是它最大的局限性.
  • 另外内敛函数和非内联函数还有一个重要的区别,那就是内联函数所引用的Lambda表达式中时可以使用return关键字进行函数返回.
  • 将高阶函数声明称为内联函数是一种良好的编程习惯,事实上大多数的高阶函数都可以直接声明成为内联函数,但是也是有少部分情况是例外的:
inline fun runRunnable(block: () -> Unit) {val ruunable = Runnable {block()}runnable.run()
}
  • 这段代码在没有加上inline关键字的时候肯定是没有问题,但是将这个高阶函数声明成内联函数就会出现下面的问题

  • 上述代码出现问题的主要原因是:在runRunnable()函数中,我们创建了一个Runnable对象,并在Runnable的Lambda表达式中调用了传入的函数类型参数.
  • 而Lambda表达式在编译的时候会转换成为匿名类的实现方式,也就是说,上述代码实际上是在匿名类中调用了传入的函数类型参数.
  • 而内联函数所引用的Lambda表达式允许使用return关键字进行函数返回,但是由于我们是在匿名内部类中调用的函数类型的参数,此时是不可能进行外层调用函数返回的,最多只能对匿名类中的调用函数进行返回
  • 也就是说我们在高阶函数当中创建了另外Lambda或者匿名类的实现,并且在这些实现中调用函数类型参数,此时再将高阶函数声明成内联函数,就一定会提示错误.
  • 所以在这个时候,我们如果还想要使用内联函数的时候,就需要借助crossinline关键字就可以很好的解决问题.
inline fun runRunnable(crossinline block: () -> Unit) {val ruunable = Runnable {block()}runnable.run()
}
  • 这个关键字其实就相当于一个契约一样,它用于保证在内敛函数的Lambda表达式一定不会使用return关键字,这样冲突就不存在了.

Kotlin小知识之高阶函数相关推荐

  1. Kotlin学习三:高阶函数

    目录 一.高阶函数的基本概念 二.常见高阶函数 1.关于list映射 2.flatMap 3.综合1 4.综合2 三.尾递归优化 四.闭包 五.函数复合 六.科理化 七.偏函数 八.小案例 一.高阶函 ...

  2. [译]Effective Kotlin系列之探索高阶函数中inline修饰符(三)

    简述: 不知道是否有小伙伴还记得我们之前的Effective Kotlin翻译系列,之前一直忙于赶时髦研究Kotlin 1.3中的新特性.把此系列耽搁了,赶完时髦了还是得踏实探究本质和基础,从今天开始 ...

  3. Kotlin 编程核心基石—高阶函数

    前言 1. 高阶函数有多重要? 高阶函数,在 Kotlin 里有着举足轻重的地位.它是 Kotlin 函数式编程的基石,它是各种框架的关键元素,比如:协程,Jetpack Compose,Gradle ...

  4. kotlin高阶函数的初级理解

    1.定义 如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数. 系统中已经有很多,示例:库函数 在 Standard.kt 标准库中提供了一些便捷的内置高阶函数 ...

  5. kotlin修炼指南8—集合中的高阶函数

    点击上方蓝字关注我,知识会给你力量 Kotlin对集合操作类新增了很多快捷的高阶函数操作,各种操作符让很多开发者傻傻分不清,特别是看一些Kotlin的源码或者是协程的源码,各种眼花缭乱的操作符,让代码 ...

  6. Kotlin中的高阶函数

    博客地址sguotao.top/Kotlin-2018- 在Kotlin中,高阶函数是指将一个函数作为另一个函数的参数或者返回值.如果用f(x).g(x)用来表示两个函数,那么高阶函数可以表示为f(g ...

  7. kotlin 常用高阶函数

    package kotlinall.chapter5import java.io.BufferedReader import java.io.FileReader import java.lang.S ...

  8. Kotlin 使用高阶函数实现回调

    lambda 和 高阶函数 之前学习了 lambda 和高阶函数,然后在 android 开发中对 onClick 事件进行监听是一个很常用的功能,kotlin 的常规实现如下: rootView.s ...

  9. 一天一个小知识:KT高阶函数

    让我们从匿名函数聊起 我们听说过有匿名类,那作为一等公民的函数就也会有匿名函数 什么是一等公民? 我们知道Java的一等公民是类,就连一个普通的程序入口也要用类包一下.而kotlin中除了类是一等公民 ...

最新文章

  1. c语言:输出一个菱形图
  2. OKR的本质是什么?目标如何制定?
  3. 李德毅院士:智能时代的农机驾驶——人工智能一百年
  4. 整理一份程序员常用的各类工具、技术站点
  5. MongoDB 优化器profile
  6. java FileI(O)nputStream为什么比BufferedI(O)utputStream慢?
  7. ajax保存避免重复提交,ajax 实现防止重复提交
  8. 【Android】笔记
  9. android中白色怎么表示,android – 将位图中特定颜色以外的所有颜色转换为白色...
  10. Latex初学入门记载
  11. html+表格+左侧表头,HTML多表头表格代码
  12. 使用Java抓取解析汽车之家车型配置数据
  13. 查看磁盘文件夹大小工具WinDirStat
  14. Python文本分析 jieba
  15. android rgb接口,Android RGB颜色查询对照表
  16. 一、Docker 容器
  17. 网络运维工程师 ,需要掌握知识的总结。
  18. 可达性分析算法(自用)
  19. STM32玩转物联网实战篇:01.网络通信前准备
  20. Cholesterol-PEG-Maleimide|胆固醇-聚乙二醇-马来酰亚胺修饰蛋白用

热门文章

  1. 运用物理信息神经网络求解流体力学方程
  2. 2022年注册会计师考试全科汇总测试题及答案
  3. 小游戏市场被引爆,如何利用才能正确解锁?
  4. 服务器中”系统平均负载 Load average“含义学习
  5. 杂题记录及简要题解(一)
  6. Kindle 2 国际版
  7. 西安财经大学计算机科学学院,西安财经学院
  8. cygwin 查找ip地址_NBtscan扫描整个局域网IP及MAC地址
  9. Debug: Minkowski undefined symbol; Tensorboard has no attribute ‘version‘
  10. 分布式任务调度平台XXL-JOB的简单使用