持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情
高阶函数
在我们前面的Compose学习,你会发现Compose函数有些时候会接收Lambda表达式作为参数,这种接收Lambda参数的函数就可以称为具有函数式编程风格的API,如果在Kotlin中我们需要定义自己的函数式API,我们就需要借助Kotlin的高阶函数来实现了,下面我们来了解以下什么是高阶函数
高阶函数:如果一个函数接收另一个函数作为参数,或者返回值类型为另外一个函数,那么该函数就称为高阶函数。怎么能让一个函数作为另外一个函数作为参数呢?这就会引入Kotlin中新增的一个概念函数类型,我们在平常的开发中使用过基本数据类型,引用数据类型。函数类型又是什么呢?函数类型即使用函数作为参数,或者是返回值。
高阶函数定义
(参数列表) -> 返回值
(String,Int) -> Unit //接收两个类型的参数,分别为String,Int 返回之为Unit的函数
将这样的定义放到我们常规函数定义的参数列表或者返回值中,那么这个函数就可以被称为一个高阶函数了
fun example(onClick:(String,Int)->Unit){
onClick("hello",0)
}
可以看到我们的example()接收了一个函数类型的参数,我们将函数名定义为onClick,onClick函数接收两个类型的参数并且返回值为Unit,我们在调用的时候只需要向常规函数调用一样即可。
使用场景
上面我们既然学会了怎么定义高阶函数,那么官方是怎么使用的呢
-
使用高阶函数替代接口回调
//平时我们使用最常用的就是给View设置监听,以前我们的写法就是通过匿名类,或者实现接口等的方式来实现,下面就是Kotlin的实现方式 tV.setOnClickListener { //doSomeThing }上面会包含两个kotlin的语法糖,一个是SAM转换和尾随Lambda
-
为函数体提供上下文
fun StringBuilder.build(block:StringBuilder.()->Unit):StringBuilder{ block() } fun main(){ val sb = StringBuilder.build{ append("hello") append(",world") } } -
Collection的很多操作符都是接收一个函数类型的参数
fun main() { val list = listof(1, 2, 3, 4) val all = arrays.count { it > 2 } println(all) }
等等高阶函数的使用姿势还有许多。这里就不一一赘述
内联函数
内联函数的概念有点和C语言中的宏定义有点相似,在编译时会将函数体直接自动替换到调用它地方。怎么将函数声明成内联函数呢,只需要在定义函数时使用inline关键字
fun add() {
printString()
}
//普通函数中并不推荐加inline关键字
inline fun printString() {
printString("params")
}
通过反编译之后查看java代码
public final void add(params:Int,params1:Int) {
String var3 = "params";
System.out.println(var3);
}
我们都知道一般函数在被调用的时候都会创建一个栈帧(并且有相应的进出栈的操作)
(Java中调用函数会创建一个栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态链接、方法出口等信息
每一个方法被调用直至执行完成的过程,就对应着一个栈帧入栈、出栈的过程。这一个过程是会有性能消耗的)
inline关键字的出现就是为了优化这种消耗的,可以看出使用inline关键字声明的函数并不会出现函数调用,而是会在编译器见进行代码替换,但是在普通函数上这种提升并不是特别明显,内联函数的出现是为了配合高阶函数而使用的,我们都知道我们的Lambda在编译成编码时会被转换成一个Function的匿名类,我们每调用一次都会创建一个匿名类对象,这样就会带来额外的开销,那么使用inline之后自动将代码替换到它的调用处,这样也就不会出现运行时的开销了。
所以一般我们定义高阶函数时都会声明为内联函数。