Kotlin 高阶函数

高阶函数的定义: 如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数。
如何定义一个函数类型:(String, Int) -> Unit ,->左边的部分就是用来声明该函数接收什么参数的,如果不接收任
何参数写一对空括号就可以了。->右边的部分用于声明该函数的返回值是什么类型,如果没有返回值就使用 Unit,它大致。

举个栗子:

                                   // 函数参数名 
fun num1Andnum2(num1: Int, num2: Int, operator: (Int, Int) -> Int): Int {
    val result = operator(num1, num2)
    return result
}

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

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

fun main() {
    val num1 = 100
    val num2 = 200
    val result1 = num1Andnum2(num1, num2, ::plus)
    val result2 = num1Andnum2(num1, num2, ::minus)
    println("result1 is $result1")// 300
    println("result2 is $result2")// -200
}

注意调用 num1AndNum2() 函数的方式,第三个参数使用了 ::plus 和 ::minus 这种写法。这是一种函数引用方式的写法,表示将 plus() 和 minus() 函数作为参数传递给 num1AndNum2() 函数。

上述这种函数引用的写法虽然能够正常运行,但是如果每次调用任何高阶函数的时候都还得定义一个与其类型参数相匹配的函数,这样太复杂了。
因此 Kotlin 还支持其他多种方式来调用高阶函数,比如 Lambda 表达式、匿名函数、成员引用等。

使用 Lambda 表达式调用改写上述代码:

fun num1Andnum2(num1: Int, num2: Int, operator: (Int, Int) -> Int): Int {
    val result = operator(num1, num2)
    return result
}

fun main() {
    val num1 = 100
    val num2 = 200
    val result1 = num1Andnum2(num1, num2) { n1, n2 ->
        n1 + n2 // 最后一行作为返回值
    }
    val result2 = num1Andnum2(num1, num2) { n1, n2 ->
        n1 - n2
    }
    println("result1 is $result1")// 300
    println("result2 is $result2")// -200
}

使用高阶函数实现标准函数apply的功能

标准函数 apply 功能:他可以用于给 Lambda 表达式提供一个指定的上下文,当需要连续调用同一个对象的多个方法时,apply 函数能让代码变得更加精简。

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

fun main() {
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
    val result = StringBuilder().build {
        append("Start.\n")
        for (fruit in list) {
            append(fruit).append(" ")
        }
        append("\nEnd.\n")
    }
    println(result.toString())
}

这里我们给 StringBuilder 类定义了一个 build 扩展函数,这个扩展函数接受一个函数类型参数,并且返回值类型也是 StringBuilder。注意这个函数类型参数的声明和我们之前学习的语法有所不同:他在函数类型前面加上了一个 StringBuilder. 的语法结构。这是什么意思呢?其实这才是定义高阶函数完整的语法规则,在函数类型前面加上 ClassName. 就表示这个函数类型是定义在哪个类当中的,那么这里将函数类型定义到 StringBuilder 类当中有什么好处呢?好处就是当我们调用 build 含糊时传入的 Lambda 表达式将会自动拥有 StringBuilder 的上下文,同时这也是 apply 函数的实现方式。

此时我们的 build 函数只能作用在 StringBuilder 类上面,而 apply 函数是可以作用在所有类上面的。如果要实现 apply 函数的这个功能,需要借助 Kotlin 泛型才行。

内联函数的作用

我们使用的 Lambda 表达式在底层是被转换成了匿名类的实现方式。这就表明,我们每调用一次 Lambda 表达式,都会创建一个新的匿名类实例,当然也会造成额外的内存和性能开销。为了解决这个问题,Kotlin 提供了内联函数的功能,它可以将使用 Lambda 表达式带来的运行时开销完全消除

内联函数的用法非常简单,只需要在定义高阶函数时加上 inline 关键字的声明即可:

inline fun num1Andnum2(num1: Int, num2: Int, operator: (Int, Int) -> Int): Int {
    val result = operator(num1, num2)
    return result
}

noinline 与 crossinline

noinline
如果一个高阶函数接收两个或者更多函数类型参数,这是给函数加上 inline 关键字,那么 Kotlin 编译器会自动将所有引用的 Lambda 表达式全部进行内联。但是,如果我们只想内联其中的一个 Lambda 表达式该怎么办呢?这时就可以使用 noinline 关键字:

inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit) {}

内联函数 和 非内联函数 的区别:内联函数所引用的 Lambda 表达式中是可以使用 return 关键字来进行函数返回的,而非内联函数只能进行局部返回。

fun printString(str: String, block: (String) -> Unit) {
    println("printString begin")
    block(str)
    println("printString end")
}
fun main() {
	printString("") { s ->
        println("lambda start")
        if (s.isEmpty()) return@printString
        // Lambda表达式剩余不执行
        println(s)           
        println("lambda end")
    }
    println("main end")
 }
/*
main start
printString begin
lambda start
printString end
main end
*/

Lambda 表达式中是不允许直接使用 return 关键字的,这里使用 return@printString 的写法,表示进行局部返回,并且不再执行 Lambda 表达式的剩余代码。

现在将 printString 改为内联函数:

inline fun printString(str: String, block: (String) -> Unit) {
    println("printString begin")
    block(str)
    println("printString end")
}

fun main() {
	printString("") { s ->
        println("lambda start")
        if (s.isEmpty()) return
        // Lambda表达式剩余不执行
        println(s)           
        println("lambda end")
    }
    println("main end")
 }
/*
main start
printString begin
lambda start
*/

现在 printString() 函数变成了内联函数,我们就可以在 Lambda 表达式中使用 return 关键字了。此时 return 代表的是返回外层的调用函数,也就是 main() 函数。

crossinline
声明了crossinline 之后,我们就无法在调用函数时的 Lambda 表达式中用 return 关键字进行函数返回了,但是仍然可以使用 return@printString 的写法进行局部返回

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值