第一行代码 第三版 第6章 6.5Kotlin课堂 :高阶函数

6.5 高阶函数

6.5.1 定义高阶函数

高阶函与Lambda的关系密不可分。
接受Lambda参数的函数可以称为具有函数式编程风格的API。
如果要定义自己的函数式API,就需要借助于高阶函数来实现。
什么是高阶函数:如果一个函数接受另一个函数作为参数,或者返回值类型是另一个函数。
kotlin中的新类型函数类型,函数类型的语法规则为:
(String, Int) -> Unit
-> 左边的部分是用来声明该函数接收什么参数的,多个参数之间用逗号隔开。如果不接受参数,需要写一对空括号。
->右边的部分是用来声明该函数的返回值是什么类型,如果没有返回值就使用Unit标识。

将该函数类型作为参数传入其他函数或者作为返回值声明,那么就实现了一个高阶函数。

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

即使是同一个高阶函数,只要传入不同的函数类型参数,那么它的执行逻辑和最终的返回结果就可能是完全不同的。

使用书上的例子:


//该函数有三个参数,最后一个参数是函数类型的
fun num1AndNum2(num1:Int,num2:Int,operation:(Int,Int)->Int):Int{
    val result=operation(num1,num2)
    return result
}

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

fun main() {
    //在对高阶函数进行传参是使用符号“::”来引用函数
    val result= num1AndNum2(5,8,::plus)
    val result1= num1AndNum2(8,3,::minus)
    println("${result}----${result1}")
}

为了让函数引用的写法更加简单(现在的写法:得先定义一个与其函数类型参数相匹配的函数):Kotlin还支持了其他方式来调用高阶函数,Lambda表达式,匿名函数,成员引用等。
使用Lambda表达式实现上述代码:

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

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

使用高阶函数模仿实现一个类似apply()的功能

fun StringBuilder.build(block :StringBuilder.() ->Unit):StringBuilder{
//在函数类型的前面加上ClassName. 表示这个函数类型是定义在哪个类中的
    block()
    return this
}

fun main(){
    val list = listOf<String>("Apple","Banana","Pear","Orange","Grape")
    val result=StringBuilder().build{
    //Lambda表达式
        append("Start eating fruits.\n")
        for(fruit in list){
            append(fruit).append("\n")
        }
        append("Ate all fruits.")
        }
    println(result.toString())
}

6.5.2 内联函数

Lambda表达式在底层被转换成了匿名类的实现方式,我们每次调用Lambda表达式都会创建一个新的匿名类。这样就会造成额外的内存和性能开销。
为了解决这个问题,Kotlin提供的内联函数就可以解决。

内联函数的用法:在定义高阶函数是加上inline关键字

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

Kotlin编译器 会将内联函数中的代码在编译的时候自动替换到调用它的地方。
(我自己的理解:应该就为了避免反复创建匿名类,而直接将函数中的代码用于调用函数的地方)。

如果高阶函数接受多个参数,要是加上inline关键字,会自动对所有引用的Lambda表达式进行内联。

只想内联其中的一个参数,我们就需要使用noinline关键字对其他参数进行修饰。

内联的函数类型参数在编译的时候就会被进行代码替换,因此他没有真正的参数属性。非内联的函数类型可以自由地传递给其他任何函数。因为他是一个真实的参数,而内联的函数类型只允许传递给另一个内联函数。

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

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

fun main() {
    println("main start")
    val str = ""
    printString(str){s->
        println("Lambda start")
        if(s.isEmpty()) return@printString// return@printString 表示局部返回
        println(s)
        println("Lambda end")
    }
    println("main end")
}
运行结果:
main start
printString begin
Lambda start
printString end
main end

return@printString 表示局部返回
在代码中,进行了局部返回,跳出了Lambda表达式。

将函数声明成inline内联函数:

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

fun main() {
    println("main start")
    val str = ""
    printString(str){s->
        println("Lambda start")
        if(s.isEmpty()) return
        println(s)
        println("Lambda end")
    }
    println("main end")
}
运行结果:
main start
printString begin
Lambda start

这表明,该次是返回外层函数的调用函数。
(我感觉内联函数替换后的代码是:

fun main() {
    println("main start")
    val str = ""
    println("printString begin")
    println("Lambda start")
    if(str.isEmpty()) return
    println(str)
    println("Lambda end")
    println("printString end")
    println("main end")
}

)这样就比较好解释了。。。。

还有一个关键字crossinline 用于保证内联函数的Lambda表达式中一定不会使用return关键字。

inline fun runRunnable(crossinline block: () -> Unit){
    val runnable = Runnable{
        block()
    }
    runnable.run()
}
发布了28 篇原创文章 · 获赞 11 · 访问量 2405

猜你喜欢

转载自blog.csdn.net/Y_an_Y/article/details/105624731