Android开发(Kotlin) 高阶函数、内联函数

目录

高阶函数

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

kotlin有函数类型。

函数类型语法规则

kotlin 复制代码
(String,Int)->Unit

->左边部分用来声明改函数接收什么参数,多个参数之间用逗号类隔开。->右边部分用于声明该函数的返回值是什么,如果没有返回值就使用Unit。

示例

kotlin 复制代码
    fun example(func:(String, Int) -> Unit) {
        
        func("hello",456)
    }

高阶函数的用途允许让函数类型的参数来决定函数的执行逻辑。同一个高阶函数,只要传入不同的函数类型参数,那么它的执行逻辑和最终结果可能就完全不同。

定义一个方法,传入两个整形和一个函数类型,由传入的函数类型参数来决定做什么运算。

kotlin 复制代码
    fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {

        val result = operation(num1,num2)
        return  result
    }

定义与上面函数类型相匹配的函数。

kotlin 复制代码
    fun plus(num1: Int, num2: Int): Int {

        return num1 + num2
    }

    fun minus(num1: Int, num2: Int): Int {

        return num1 - num2
    }

使用num1AndNum2函数

kotlin 复制代码
fun main() {

    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1, num2, ::plus)
    val result2 = num1AndNum2(num1, num2, ::minus)

    println("result1 is $result1,result2 is $result2")
}

结果

kotlin 复制代码
result1 is 180,result2 is 20

Process finished with exit code 0

::plus,是一种函数引用的写法。
lambda表达式是常用的高阶函数调用方式。

kotlin 复制代码
fun main() {

    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1, num2) { num1, num2 ->
        num1 + num2
    }
    val result2 = num1AndNum2(num1, num2) { num1, num2 ->

        num1 - num2
    }

    println("result1 is $result1,result2 is $result2")
}

使用高阶函数模拟类似apply函数

kotlin 复制代码
fun StringBuilder.build(block: StringBuilder.() -> Unit): StringBuilder{
    block()
    return  this
}

这是一个扩展函数,这个函数接收了一个函数型参数。这个函数类型参数有一点不同,在函数类型的前面加上了一个StringBuilder,表示这个函数类型是定义在那个类中的。

函数类型定义到Stringbuilder类中,好处是传入的lambda表达式将会自动拥有Stringbuilder的上下文。

示例代码

kotlin 复制代码
fun main() {

    val list = listOf<String>("Apple", "Banana", "Orange")
    val result = StringBuilder().build {
        append("Start\n")

        for (str in list) {

            append(str).append("\n")
        }
        append("end\n")
    }
    println(result.toString())
}

结果

kotlin 复制代码
Start
Apple
Banana
Orange
end


Process finished with exit code 0

内联函数的作用

kotlin的高阶函数每次调用都会创建一个匿名类,会造成额外的开销,内联函数可以将lambda表达式带来的运行时开销完全消除。

在定义的高阶函数前面加上inline关键字即可。

kotlin 复制代码
inline  fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {

    val result = operation(num1, num2)
    return result
}

noinline与crossinline

noinline

当一个高阶函数有多个函数类型的参数,我们只想内联其中一个函数类型的参数,就要使用noinline。

kotlin 复制代码
inline  fun inlineTest(block1:()-> Unit,noinline block2: () -> Unit){

}

为什么有noinline关键字来排除内联功能?因为内联的函数类型参数在编译的时候会被进行代码替换,因此它没有真正的参数属性。非内联的函数类型参数可以自由地传递给其他任何函数,因为它就是一个真实的参数,而内联的函数类型参数值允许传递给另外一个内联函数。

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

示例

kotlin 复制代码
fun main() {

    println("main start")
    val str= ""
    printString(str){s ->
        println("lambda start")
        if(s.isEmpty()){

            return@printString//局部返回
        }
        println("lambda end")
    }
    println("main end")
}


fun printString(str: String, block: (String) -> Unit) {

    println("printString begin")
    block(str)
    println("printString end")
}

执行结果

kotlin 复制代码
main start
printString begin
lambda start
printString end
main end

Process finished with exit code 0

查看结果,lambda end没有被打印出来,printString end被打印出来了,说明return@printString只能进行局部返回。

将printString()函数声明称成一个内联函数

kotlin 复制代码
fun main() {

    println("main start")
    val str= ""
    printString(str){s ->
        println("lambda start")
        if(s.isEmpty()){

            return
        }
        println("lambda end")
    }
    println("main end")
}

inline  fun printString(str: String, block: (String) -> Unit) {

    println("printString begin")
    block(str)
    println("printString end")
}

执行结果

kotlin 复制代码
main start
printString begin
lambda start

Process finished with exit code 0

此时的return代表的事返回外层的调用函数,就是main().

crossinline

kotlin 复制代码
inline  fun runRunnable(block: () -> Unit) {

    val runnable = Runnable {
        block()
    }
}

上面这段代码没有inline 可以正常运行,加上inline 就报错。

原因解释,在runRunnable()函数中,创建了一个Runnable对象,并在Runnable的Lambda表达式中调用了传入的函数类型参数,而lambda表达式在编译的时候会被转换成匿名类的实现方式。内联函数所引用的lambda表达式允许使用return关键字进行函数返回,但由于是在匿名类中调用的函数类型参数,是不能进行外层调用函数返回的,只能对匿名类中的函数进行返回,所以提示错误。

这种情况使用crossinline关键字就可以编译通过。

kotlin 复制代码
inline  fun runRunnable(crossinline  block: () -> Unit) {

    val runnable = Runnable {
        block()
    }
}

crossinline的作用,因为内联函数的lambda表达式中允许使用return关键字,和高阶函数的匿名类实现中不允许使用return关键字之间造成了冲突。而crossinline关键字就像一个契约,用于在内联函数的lambda表达式中一定不会使用return关键字。

声明crossinline之后,无法在调用runRunnable()函数时的lambda表达式中使用return关键字进行函数返回,但仍可以使用return@runRunnable的写法来进行局部返回。

相关推荐
2501_915921432 小时前
Fastlane 结合 开心上架(Appuploader)命令行版本实现跨平台上传发布 iOS App 免 Mac 自动化上架实战全解析
android·macos·ios·小程序·uni-app·自动化·iphone
Murphy_lx2 小时前
C++ thread类
开发语言·c++
彩妙不是菜喵2 小时前
C++ 中 nullptr 的使用与实践:从陷阱到最佳实践
开发语言·jvm·c++
lskisme3 小时前
springboot maven导入本地jar包
开发语言·python·pycharm
雨白3 小时前
重识 Java IO、NIO 与 OkIO
android·java
开心-开心急了3 小时前
pyside6实现win10自动切换主题
开发语言·python·pyqt·pyside
沐知全栈开发3 小时前
Foundation 模态框
开发语言
wjs20243 小时前
CSS 导航栏
开发语言
啦啦9117144 小时前
Niagara Launcher 全新Android桌面启动器!给手机换个门面!
android·智能手机