开启成长之旅!这是我参与「日新计划 2 月更文挑战」的第 16 天,点击查看活动详情

引言

前面我们看过了安卓Kotlin的高阶函数的定义(安卓开发之Kotlin学习——初遇高阶函数 – ()),但是还没在实际开发环境使用,本篇我们一起来看看高阶函数使用的实例与使用方法,继续学习Kotlin高阶函数。

正文

首先让我们先写一个简单的高阶函数:

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

我们在高阶函数num1AndNum2里传入三个参数,分别两个整型和最后一个函数类型参数operation,operation是一个可以传入两个Int参数并返回Int类型值的函数类型参数,然后num1AndNum2返回值为Int类型,在结构体中将该返回值设置为传入num1和num2的operation函数返回值。

如果要调用上述高阶函数,我们必须为其传入一个(Int, Int) -> Int类型的函数,所以我们可以在定义与之相匹配的函数:

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

于是我们定义了上面两个函数,分别是两数相加和两数相减,接下来我们看看调用高阶函数的方式:

val num1 = 200
val num2 = 100
val addNum = num1AndNum2(num1, num2, ::plus)
val minusNum = num1AndNum2(num1, num2, ::minus)

我们看到上面的调用方式与我们平常的函数调用有些许不同,我们在传递第三个参数(函数类型)是在需要调用的函数的函数名前加了符号“::”,这是一种函数引用方式的写法,表示将我们的对应函数作为参数传入高阶函数中,而我们定义的高阶函数就会以传入的函数类型参数来决定具体的运算逻辑,这么一看,其实我们上面的例子就是废话文学,实际上还是调用了plus()和minus()函数去运算,不过包了一层高阶函数罢了,当然,这里只是wield学习来举例高阶函数的使用,这个简单的例子也更加方便学习。

上面的写法显得很啰嗦,每次去定义新的函数来为高阶函数传递,实在太麻烦,所以下面我们将使用之前说过的Lambda表达式(安卓语言基础之Kotlin高阶函数——Lambda表达式(一) – ()、安卓语言基础之Kotlin高阶函数——Lambda表达式(二) – ())去简化上面的写法,对,我们的高阶函数也支持Lambda表达式,而且这似乎是最常见和最普遍的高阶函数调用方式:

val num1 = 200
val num2 = 100
val addNum = num1AndNum2(num1, num2) { n1, n2 ->
    n1 + n2
}
val minusNum = num1AndNum2(num1, num2) { n1, n2 ->
    n1 - n2
}

Lambda表达式的写法就可以省去我们重新定义函数的步骤。

在之前安卓语言基础之Kotlin高阶函数——Lambda表达式(二) – ()的实际应用中,我们使用了apply函数,它可以用于给Lambda表达式提供一个指定的上下文,当连续调用同一个对象的多个方法时它就会让代码更为精简,如下:

安卓Kotlin开发学习——接着看高阶函数
就拿上面的例子来说,我们可以先给StringBuilder类定义一个扩展函数build,让这个扩展函数来接受一个函数类型参数,返回值类型也同样是StringBuilder,这样我们就能用高阶函数去实现于apply函数类似的功能:

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

我们可以看到上面的代码中与我们之前声明函数类型的方式不一样,它在函数类型前加了一个StringBuilder. 的语法结构,其实这才是定义高阶函数完整的语法规则,要在函数类型的前面加上ClassName,这就能明确表示这个函数类型是定义于哪个类中。

而我们此处加类名的意义在于,我们如果明确了StringBuilder类中,那我们在调用高阶函数build时传入的Lambda表达式将能自动拥有StringBuilder的上下文,同时这也是apply函数的实现方式,但我们写的也只能作用于StringBuilder类,而apply函数是可以作用于所以类上面,如果想实现这个效果,我们还得借助Kotlin的泛型,所以后面我们的文章中会分享一些泛型的知识。

我们接着看调用的实例:

val listA = listOf("Apple", "Pear", "Banana")
val resultA = StringBuilder().build {
    append("start add. \n")
    for (fruit in listA) {
        append(fruit).append("\n")
    }
    append("All fruits.")
}
println(resultA.toString())

我们可以看到和apply函数的调用并无区别。

内联函数

我们在前面调用Kotlin的高阶函数时使用Lambda表达式,其实从Java层来说,Kotlin还是使用了Java的匿名函数,每调一次Lambda表达式,就会创建一个新的匿名类实例,而这也将会造成额外的内存和性能开销。

为了消除这一影响,Kotlin就提供了内联函数的方法,它能将使用的Lambda表达式带来的运行时开销消除,实现方法很简单,我们用到关键字inline,并将其加在我们定义的高阶函数前面即可:

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

总结

这里我们主要介绍完了Kotlin高阶函数的定义方法和简单使用,还提了一下内联函数,但没有细说,后面还会在本文进行补充,将一些情况补上。