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的写法来进行局部返回。

相关推荐
同学小张3 小时前
【端侧AI 与 C++】1. llama.cpp源码编译与本地运行
开发语言·c++·aigc·llama·agi·ai-native
踢球的打工仔4 小时前
PHP面向对象(7)
android·开发语言·php
安卓理事人4 小时前
安卓socket
android
汤姆yu6 小时前
基于python的外卖配送及数据分析系统
开发语言·python·外卖分析
Yue丶越6 小时前
【C语言】字符函数和字符串函数
c语言·开发语言·算法
翔云 OCR API7 小时前
人脸识别API开发者对接代码示例
开发语言·人工智能·python·计算机视觉·ocr
V***u4537 小时前
MS SQL Server partition by 函数实战二 编排考场人员
java·服务器·开发语言
这是程序猿7 小时前
基于java的ssm框架旅游在线平台
java·开发语言·spring boot·spring·旅游·旅游在线平台
芳草萋萋鹦鹉洲哦7 小时前
【elemen/js】阻塞UI线程导致的开关卡顿如何优化
开发语言·javascript·ui
爱学习的小邓同学7 小时前
C++ --- 多态
开发语言·c++