Kotlin中的高阶函数

183 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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之后自动将代码替换到它的调用处,这样也就不会出现运行时的开销了。

所以一般我们定义高阶函数时都会声明为内联函数。