目录
高阶函数
如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,则改函数就称为高阶函数。
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的写法来进行局部返回。