上一篇:Kotlin一步一步学(四) -- 扩展函数和运算符
定义
如果一个函数接收另一个函数作为参数,或者返回值的类型是另外一个函数,那么该函数就称为高阶函数
不同于定义一个普通的字段类型,函数类型参数的语法是有点特殊的,基本规则如下:
(String, Int)-> Unit
将上述的函数类型添加到某个函数的参数声明或者返回值声明上,那么这个函数就是一个高阶函数了。具体如下所示:
fun example(func : (String , Int) -> Unit){
func("hello world",123)
}
这里的example()函数接收了一个函数类型的参数,因此example()函数就是一个高阶函数。而调用一个函数类型的参数,它的语法类似于调用一个普通的函数,只需要在参数名的后面加上一对括号,并在括号中传入必要的参数就可以了。很简单吧。。。
用途
高阶函数允许让函数类型的参数来决定函数的执行逻辑。也就是说即使是同一个高阶函数,只要传入不同的函数类型参数,那么它的执行逻辑和最终的返回结果就可能是完全不同的。
案例:定义一个叫作num1AndNum2()的高阶函数,并让它接收两个整型和一个函数类型的参数。我们会在num1AndNum2()函数中对传入的两个整型参数进行某种运算,并返回最终的运算结果,但是具体进行什么运算是由传入的函数类型参数决定的。具体num1AndNum2()的实现和调用方法如下所示:
//高阶函数
fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
return operation(num1, num2)
}
fun plus(num1: Int, num2: Int): Int {
return num1 + num2
}
fun minus(num1: Int, num2: Int): Int {
return num1 - num2
}
fun main() {
val num1 = 200;
val num2 = 100;
val result1 = num1AndNum2(num1, num2, ::plus)
val result2 = num1AndNum2(num1, num2, ::minus)
println("result1 is $result1") //输出结果为:result1 is 300
println("result2 is $result2") //输出结果为:result2 is 100
}
这里调用num1AndNum2()函数时,第三个参数使用了::plus和::mius这种写法。这是一种函数引用方式的写法,表示将plus()和minus()函数作为参数传递给num1AndNum2()函数。而由于num1AndNum2()函数中使用了传入的函数类型参数来决定具体的运算逻辑,因此这里实际上就是分别使用了plus()和minus()函数来对这两个数字进行运算。
上述代码虽然能够正常的运行,但是每次调用的时候,还得先定义一个与其函数类型参数匹配的函数,这样太麻烦了。下面我们利用Lambda表达式对其进行改写,代码如下所示:
fun main() {
val num1 = 200
val num2 = 100
val result1 = num1AndNum2(num1, num2) { n1, n2 ->
n1 + n2
}
val result2 = num1AndNum2(num1, num2) { n1, n2 ->
n1 - n2
}
println("result1 is $result1") //输出结果为:result1 is 300
println("result2 is $result2") //输出结果为:result2 is 100
}
看起来是是不是如此优美,如此美妙。
内联函数
Java中并没有高阶函数的概念,所以Kotlin会对高阶函数进行转换,变成Java编译器能读懂的语法结构。事实上我们一直使用的Lambda表达式在底层会被转换成匿名函数的形式。这就说明,我们每调用一次Lambda表达式,都会创建一个新的匿名类实例,当然这样就必然会造成额外的内存和性能开销。
为了解决这个问题,Kotlin提供了内联函数的功能,它可以把Lambda表达式带来的运行时开销完全消除掉。
inline
内存函数的用法非常简单,只需要在高阶函数上加上inline
关键字声明就可以,具体如下所示:
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
return operation(num1, num2)
}
使用内联函数后,Kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样就不存在运行时的开销了。我们对上面的代码安装编译器的编译规则进行替换,代码如下所示:
fun main() {
val num1 = 200
val num2 = 100
val result1 = n1 + n2
val result2 = n1 - n2
println("result1 is $result1") //输出结果为:result1 is 300
println("result2 is $result2") //输出结果为:result2 is 100
}
使用内联函数后,如果在lambda表达式中使用了return关键字,此时的return代表的是返回最外层的调用函数。 而如果不使用内联函数,lambda表达式中的return只是表示局部返回,最外层的调用函数还是会继续执行。
内联函数还有noinline和crossline两个关键字,这里就不做详细介绍了,遇到时请自行搜索。