高阶函数
定义:如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,该函数称为高阶函数。
Kotlin增加了一个函数类型概念,也就是说可以传递函数作为参数
基本规则
1 | //基本规则 |
解释: -> 左边部分用来声明该函数接收什么参数,多个参数之间用逗号隔开。 右边部分用于声明该函数返回值是什么类型,如果没有就是 Unit 相当于java中的Void
1 | fun example(block : (String, Int) -> Unit){ |
使用
1 | fun num1AndNum2(num1:Int,num2:Int, operation: (Int,Int)->Int):Int{ |
这里 ::plus ,::minus 的写法是函数的引用写法,表示将plus(),minus()函数作为参数传递给num1AndNum2()函数中。
简化:显然不可能每次使用高阶函数都要创建新的函数,这样太麻烦了。因此可以这么写。
1 | fun main(){ |
内联函数: inline
简单分析下高阶函数的实现原理
1 | //Kotlin会把num1AndNum2高阶函数,大致转换为java的这种写法 |
用法:
1 | //非常简单只需要在定义高阶函数时加上inline关键字即可 |
noinline与crossinline
前面说明了,inline是直接将Lambda表达式替换到了调用的地方,减小了内存开销。考虑一个情形:如果高阶函数接收了两个或更多的函数类型参数,此时加上了inline关键字,Kotlin编译器会自动将所有的引用全部进行内联。如果我们只想让它内联其中一个Lambda表达式怎么办呢?
这时就可以考虑使用noinline:
1 | inline fun inlineTest(block1 : () -> Unit, noinline block2: () -> Unit){} |
block2 参数前加上了noinline关键字,现在就对block1进行内联,block2就不会了。
为什么Kotlin要提供noinline关键字
由于内联函数需要进行代码替换,因此它不是真正的参数属性,非内联函数类型参数可以自由的传递给其他任何函数,因为他是一个真实的参数,而内联函数,只能传给另一个内联函数。
绝大多数高阶函数是可以直接声明成内联函数的,但也有少部分情况。
1 | inline fun runRunnable(block : () -> Unit){ |
这里会进行报错,首先,在runRunnable函数中,创建了一个Runnable对象,在Runnable的Lambda表达式当红传输了函数类型参数。而Lambda表达式在编译的时候会被转成匿名类的实现方式,实际上上述的代码是在匿名类当中调用了传入的函数类型参数。
内联函数中引用 Lambda表达式允许用return关键字进行返回,但是由于在匿名类中调用的函数类型参数,此时不能进行外层调用函数返回的,只能对匿名类中的函数调用返回。:dizzy_face::dizzy_face::dizzy_face:
此时就可以用crossinline关键字
1 | inline fun runRunnable(crossinline block : () -> Unit){ |
加上了crossinline就不会报错了,加上crossinline后,无法再调用runRunnable函数时的lambda表达式中用return进行返回了,但是可以使用 return@runRunnable进行局部返回。