Kotlin高阶函数
Kotlin高阶函数
定义高阶函数
之前的的函数式AP会接受Lambda参数,同样我们也可以自定义一个函数式API,也就是高阶函数。
高阶函数的定义是一个函数接受另一个函数作为他的参数,或者返回值是一个函数,那这个函数就是高阶函数。
那么如何定义一个函数类型呢?基本规则就是:(String, Int) -> Unit
。其中(String, Int)
表示传的参数类型,如果没有也可以为空,Unit
指的是没有返回值,也可以改成String,Int
等。
比如我们定义一个num1AndNum2()
高阶函数:
1 | fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int { |
num1,num2
很好理解就是两个参数,operation: (Int, Int) -> Int
就是我们传的函数了,代表这个函数需要传两个Int类型的参数,然后返回值为Int,然后我们定义两个函数:
1 | fun plus(num1: Int, num2: Int): Int { |
这两个函数就符合operation
这个函数类型,然后我们调用这个高阶函数:
1 | fun main() { |
其中第三个参数::plus
是函数引用方式的写法,代表将这个函数作为参数传了进去。运行结果如下:
既然我们传的是函数参数类型,我们也可以直接用lambda表达式直接传进去,就会改成:
1 | fun main() { |
这段代码表示将n1,n2
作为参数,n1-n2
(最后一行代码)作为返回值,结果与上述是一样的。
高阶函数可以被用于拓展函数中来使得代码更精简,比如apply
的用法。我们新建一个StringBuilder
的拓展函数:
1 | fun StringBuilder.build(block: StringBuilder.() -> Unit): StringBuilder { |
其中fun StringBuilder.build
就是对StringBuilder
增加了一个拓展函数,然后block: StringBuilder.()
中将函数类型定义到StringBuilder
类中的好处就是能提供上下文,使得更简单的操作,然后最后的return this
就是返回这个定义的StringBuilder
对象。然后调用:
1 | fun main() { |
就实现了类似apply
的用法了。
内联函数
在我们定义一个高阶函数时,当编译器转成Java时,会将这个函数创建一个匿名类,意思是每次调用都会创建一个增大开销,而为了避免不必要的开销,就引入了内联函数,只需要在高阶函数前加上inline
即可:
1 | inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int { |
内联函数的原理时当进行调用时会进行替换,即将n1+n2
替换到operation
处,然后再将内联函数的代码全部替换到调用的地方。
nonline与crossinline
当高阶函数被加上inline
关键字后,里面参数的所有函数类型参数,在调用时均会进行替换,但假如我们某一个函数不想被u内联呢?只需要加上nonline
关键字就好了。比如:
1 | inline fun inlineTest(block1: () -> Unit, nonline block2: () -> Unit) { |
这样就只有block1
会被内联了。那为什么有些时候我们不想被内联呢?最主要的是因为内联函数类型参数只允许传递给内联函数,而不能传递给非内联函数,比如:
1 | // 定义一个内联函数,带有一个内联的函数类型参数 |
上面代码就很好的解释了限制性。还有就是内联函数所引用的lambda表达式是可以用return
关键字进行函数返回的,而非内联函数只能进行局部返回:
1 | fun printString(str: String, block: (String) -> Unit) { |
这里使用了return@printString
表示局部返回,不再执行lambda的剩余代码,而内联函数就可以直接使用return
,因为他的本质是直接进行替换,会直接返回main()
函数,所以编译后也不会执行println("main end")
这行代码。所以大部分情况,我们都需要将高阶函数定义成内联函数。
有一种特殊情况:
1 | inline fun runRunnable(block: () -> Unit) { |
这段代码如果没有加inline
是可以正常运行的,但如果加了inline
的话就会报错:
1 | Can't inline 'block' here: it may contain non-local returns. Add 'crossinline' modifier to parameter declaration 'block |
为什么会报错呢?因为Runnable实际上是一个非内联的函数参数,但是我们将block()
这个内联函数作为参数传给了非内联函数,违反了内联函数参数的限制。因为内联函数允许使用return
关键字而非内联函数是不允许使用的。那么如何解决呢?只需要加入crossinline
关键字就可以了:
1 | inline fun runRunnable(crossinline block: () -> Unit) { |
crossinline
关键字的作用是什么呢,相当于进行了一个约定,保证内联函数的lambda表达式中一定不会出现return
关键字,就避免了冲突。这样申明后,在内联函数中仍可使用return@runRunnable
进行局部返回,但绝对不能直接使用return
。