6.5 高阶函数详解

6.5.1 定义高阶函数

如果一个函数接收另外一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数。

而这个函数形式的参数或者返回值的类型称之为:函数类型

函数类型的语法规则如下:

(String, Int) -> Unit

这个函数类型有两个形参:StringInt,返回值类型是Unit,相当于Java中的void,中间用->符号连接。

如果将上面这个函数类型添加到某个函数的参数声明或者返回值声明上,那么这个函数就是一个高阶函数了,如下所示:

fun example(func: (String, Int) -> Unit) {
    func("hello", 123)
}

高阶函数的定义如上,其中func为参数名,(String, Int) -> Unit则是参数类型。

高阶函数的用途可以简单概括为:高阶函数允许让函数类型的参数来决定函数的执行逻辑

我们编写一个高阶函数:

private fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}

另外定义两个普通函数:

fun plus(num1: Int, num2: Int): Int {
    return num1 + num2
}

fun minus(num1: Int, num2: Int): Int {
    return num1 - num2
}

很显然,plus函数和minus函数都满足num1AndNum2高阶函数的函数参数的定义,因此可以将plus函数或minus函数作为函数参数,传给num1AndNum2高阶函数。如此一来,num1AndNum2高阶函数就有了不同的执行逻辑。

@Test
fun test() {
    // 高阶函数
    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1, num2, ::plus)
    val result2 = num1AndNum2(num1, num2, ::minus)
    println("result1 is $result1")
    println("result2 is $result2")
}

这里调用num1AndNum2()函数的方式,第三个参数使用了::plus::minus这种写法。

运行程序,结果如下

result1 is 180
result2 is 20

以上这个例子就很清楚地示范了之前概括的高阶函数的用途了。

此外,Kotlin还支持其他多种方式来调用高阶函数,比如Lambda表达式,匿名函数、成员引用等。其中Lambda表达式的方式是最常见的。

@Test
fun test() {
    // 高阶函数
    val num1 = 100
    val num2 = 80
    val result3 = num1AndNum2(num1, num2){num1, num2 -> num1 + num2}
    val result4 = num1AndNum2(num1, num2){num1, num2 -> num1 - num2}
    println("result3 is $result3")
    println("result4 is $result4")
}

这样,就可以省去对之前plus函数和minus函数的定义了。

我们继续探究:用高阶函数模仿一个类似apply函数的功能。

先定义一个函数,如下

private fun StringBuilder.build(block: StringBuilder.() -> Unit): StringBuilder {
    block()
    return this
}

这里给StringBuilder定义了一个build扩展函数,且这个扩展函数是一个高阶函数。

在这个函数类型前面加上了一个StringBuilder.的语法结构,在函数类型前面加上ClassName.就表示这个函数类型是定义在哪个类当中的。

函数类型定义到哪个类当中,就自动拥有了这个类的上下文。

举一个吃水果的例子,就可以这样实现:

@Test
fun test() {
    // 完整语法规则,在函数类型前面加上ClassName.就表示这个函数类型是定义在哪个类当中
    // 好处就是这样就自动拥有了该类的上下文
    val list = listOf("apple", "Banana", "Orange", "pear", "Grape")
    val result = StringBuilder().build {
        append("Start eating fruit.\n")
        for (fruit in list) {
            append(fruit).append("\n")
        }
        append("Ate all fruits.")
    }
    println(result.toString())
}

查看输出:

Start eating fruit.
apple
Banana
Orange
pear
Grape
Ate all fruits.

到这里,高阶函数的基本用法就介绍完了。

6.5.2 内联函数的作用

6.5.3 oninline与crossinline

这两小节的内容看书理解起来困难,建议查看凯哥视频:

Kotlin 源码里成吨的 noinline 和 crossinline 是干嘛的?看完这个视频你转头也写了一吨

可以简单总结为

android kotlin高阶函数 kotlin 高阶函数原理_kotlin


附上文中代码地址