Kotlin - 高阶函数

定义高阶函数

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

定义函数类型

(String, Int) -> Unit

->左侧声明了这个函数类型需要接收的参数类型
->右侧声明了这个函数类型返回值类型
其中Unit表示没有返回值,相当于java中的void

高级函数的语法规则

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

其中example()函数就是一个高阶函数,因为它接收了参数名为func一个函数类型的参数,想使用这个函数类型时,只需要参数名后面加括号,括号中传入必要的参数就行了。

实践

先定义一个高阶函数

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

在这个函数中,将num1num2传给了第三个函数类型参数
再定义两个与这个函数类型参数完全匹配的函数

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

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

main()函数中进行调用

fun main(){
    val num1 = 5
    val num2 = 3
    println("plus result is ${num1AndNum2(num1, num2, ::plus)}")
    println("minus result is ${num1AndNum2(num1, num2, ::minus)}")
}

在这里插入图片描述
其中::plus::minus是一种函数引用方式的写法。

Lambda表达式调用高阶函数

但如果每次调用任何高阶函数的时候都还得先定义一个与其函数类型参数相匹配的函数,就复杂了。
因此Kotlin还支持其他多种方式来调用高阶函数,比如Lambda表达式、匿名函数、成员引用等。其中,Lambda表达式是最常用也是最普遍的高阶函数调用方式。
将刚刚的代码换成Lambda表达式的方式调用高阶函数:

fun main(){
    val num1 = 5
    val num2 = 3
    val result1 = num1AndNum2(num1, num2){ n1, n2 ->
        n1 + n2 //Lambda表达式的最后一行会自动作为返回值
    }
    val result2 = num1AndNum2(num1, num2){ n1, n2 ->
        n1 - n2
    }
    println("result1 is $result1")
    println("result2 is $result2")
}

此时,删掉plus()minus()函数,也能得到相同的结果:
在这里插入图片描述

高阶函数模仿apply用法

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

StringBuilder类定义了一个build扩展函数,这个扩展函数接收一个函数类型参数,并且返回值类型也是StringBuilder
定义高阶函数完整的语法规则,在函数类型的前面加上ClassName.就表示这个函数类型是定义在哪个类当中的,在上述代码中,函数类型就是定义在StringBuilder类中的。
main函数中,使用build函数简化StringBuilder构建字符串方式:

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

在这里插入图片描述

内联函数的作用

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

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

Kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样也就不存在运行时的开销了。

noinline

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

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

这里使用inline关键字声明了inlineTest()函数,原本block1block2这两个函数类型参数所引用的Lambda表达式都会被内联。但是在block2参数的前面又加上了⼀个noinline关键字,那么现在就只会对block1参数所引用的Lambda表达式进行内联了。

内联函数的局限性

内联的函数类型参数只允许传递给另外一个内联函数。

内联函数和非内联函数的区别

内联函数非内联函数
函数类型参数在编译的时候会被进行代码替换,因此它没有真正的参数属性函数类型参数可以自由地传递给其他任何函数,因为它就是一个真实的参数
引用的Lambda表达式中是可以使用return关键字来进行函数返回的只能进行局部返回,关键字return@函数名

crossinline

有些情况下,不能直接将高阶函数定义成内联函数,如下图:
在这里插入图片描述
在高阶函数中创建了另外的Lambda或者匿名类的实现,并且在这些实现中调用函数类型参数,此时再将高阶函数声明成内联函数,就一定会提示错误。
在函数类型参数的前面加上crossinline的声明,代码就可以正常编译通过了。
在这里插入图片描述
crossinline关键字用于保证在内联函数的Lambda表达式中一定不会使用return关键字,声明了crossinline之后,我们就无法在调用runRunnable函数时的Lambda表达式中使用return关键字进行函数返回了,但是仍然可以使用return@runRunnable的写法进行局部返回。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值