一、高阶函数
1.概念:一个函数作为另一个函数的参数或者返回值,那么这个拥有函数参数的函数就是一个高阶函数。Kotlin中允许一个函数中的参数是一个函数,这就是所说的高阶函数。
2.函数参数:
(Int,Int)->Int
括号里的类型代表这个参数的参数类型,括号外面的是参数函数的返回值类型。
3.实现方式
例如:
fun example(opera : (Int,Int)->Int):Int{
val num = opera(10,10)
return num
}
这样就是一个标准的高阶函数。
3.高阶函数的调用
参数中有一个函数参数,所以必须定义一个函数来作为example()函数的参数值
fun plus(num1: Int,num2: Int):Int{
return num1+num2
}
fun main(){
val result = example(::plus)
println("result1 is $result")
}
上面就是高阶函数最基本的使用方式。注意:函数参数中不能传递具体的数值。参数就是一个函数,而不是函数的实现
二、Lambda表达式调用高阶函数
Kotlin的学习肯定是离不开Lambda表达式,在高阶函数中也可以使用Lambda表达式。
Lambda表达式格式:
{参数1:参数类型,参数2:参数类型 -> 执行逻辑}
回看上面的例子,我们可以不去定义plus()函数,实现如下:
fun main(){
val result = example(){n1,n2 ->
n1+n2
}
println("result1 is $result")
}
这就是Lambda表达式在高阶函数上的应用。还有一种情况:
如果将example函数修改成:
fun example(opera : ClassName.(Int,Int)->Int):Int{
val num = opera(10,10)
return num
}
在函数参数前加上类名,就表示这个函数类型是定义在哪个类中。自动拥有这个类的上下文。
三、内联函数(inline)
内联函数实现非常简单,只需在高阶函数前面加上inline就可以了。如下:
inline fun example(opera : (Int,Int)->Int):Int{
val num = opera(10,10)
return num
}
这样就成为一个内联函数了,那么为什么要实现内联函数呢,以及内联函数有什么好处呢。
在我们使用Lambda表达式实现高阶函数时,Lambda表达式在底层会被转换成匿名类的实现方式,也就是说,我们没调用一次Lambda表达式就会创建一个新的匿名类,这就造成了内存和性能的额外开销。使用内联函数就是为了避免这个问题的。
内联函数的实现过程:
原本的内联函数:
inline fun example(opera : (Int,Int)->Int):Int{
val num = opera(10,10)
return num
}
fun main(){
val result = example(){n1,n2 ->
n1+n2
}
}
把下面红字换到上面。换成:
inline fun example(opera : (Int,Int)->Int):Int{
val num = 10 + 10
return num
}
fun main(){
val result = example()
}
接着把上面红字换到下面:
fun main(){
val result = 10 + 10
}
这就是内联函数的实现过程,避免了内存和性能的开销。
四、noinline和crossinline
1、noinline
含有多个函数参数的内联函数,Kotlin编译器回将所有引用的Lambda表达式进行内联。例如:
inline fun test(opera1 : ()->Util,opera2: () -> Unit){
}
noinline看名字就知道,可以是某一个函数参数不进行内联。
inline fun test(opera1 : ()->Util,noinline.opera2: () -> Util){
}
这样在引用opera2所对应的Lambda表达式时,就不会进行内联。
内联函数所引用的Lambda表达式中可以使用return关键字进行返回的,而非内联函数只能进行局部返回。
例如:
fun printString (opera : ()->Unit){
println("Function start")
opera()
println("Function end")
}
fun main(){
println("Main start")
printString {
println("Lambda start")
println("Lambda working")
println("Lambda end")
}
println("Main end")
}
运行结果:
进行局部返回:
fun printString (opera : ()->Unit){
println("Function start")
opera()
println("Function end")
}
fun main(){
println("Main start")
printString {
println("Lambda start")
return@printString
println("Lambda working")
println("Lambda end")
}
println("Main end")
}
非内联函数只能进行局部返回,运行结果:
Lambda表达式中return@printString后面的语句都没有被执行,其他语句被执行。
内联函数:
inline fun printString (opera : ()->Unit){
println("Function start")
opera()
println("Function end")
}
fun main(){
println("Main start")
printString {
println("Lambda start")
return
println("Lambda working")
println("Lambda end")
}
println("Main end")
}
内联函数是可以使用return的。运行结果:
return后面所有语句都没有被执行。
2、crossinline
大多数使用高阶函数时都会使用内联函数,这肯定是好的习惯。当然也有例外。
注意:如果我们在高阶函数中创建了另外的Lambda或者匿名类的实现,并且在这些实现中调用了函数类型参数,这是再将高阶函数声明称内联函数,就一定会出错。
fun crossinlineTest(opera: () -> Unit){
val runnable = object : Runnable{
override fun run() {
opera()
}
}
}
没有命名成内联函数就不会出错。如果加了inline的话。
就会出错。这是因为内联函数的Lambda中时可以使用return进行返回的。但是由于我们是在匿名类中调用了函数参数。是不可能进行外层调用函数返回的。最多只能对匿名类中的函数调用进行返回,所以一定回报错。
这是就该crossinline出场了,如下图:
这样就不会出错了。(以上代码为了符合Java实现匿名类的方式,并没有进行简化)
上面说到出错的原因是内联函数Lambda表达式中是可以使用return的,与高阶函数匿名类中不可以使用return关键字之间的冲突造成的。crossinline就是用来保证在Lambda中不会使用return这样编译就可以通过了。
所以使用crossinline就不能使用return了,但是仍然可以使用局部返回。如下:
inline fun crossinlineTest(crossinline opera: () -> Unit){
val runnable = object : Runnable{
override fun run() {
opera()
}
}
}
fun main(){
println("Main start")
crossinlineTest {
return@crossinlineTest
}
println("Main end")
}