函数式编程———内联函数
在高阶函数中参数如果是函数类型,则可以接收Lambda表达式,而Lambda表达式在编译时被编译成一个匿名类,每次调用函数时都会创建一个对象,如果函数被反复调用就会创建很多对象,会带来运行时的额外开销,为了解决此问题,在Kotlin中可以将这种函数声明为内联函数。
内联函数在编译时不会生成函数调用代码,而是用函数体中的实际代码替换每次的调用函数。
如果函数参数不是函数类型,不能接受Lambda表达式,那么这种函数一般不声明为内联函数。声明内联函数需要使用inline关键字修饰,示例如下:
//内联函数
inline fun calculatePrint(a: Int, b: Int, calc: (Int, Int) -> Int) {
println(calc(a, b))
}
fun main() {
calculatePrint(3, 5, { a, b -> a + b })
calculatePrint(3, 5) { a, b -> a - b } //如果函数最后一个参数是函数类型,则传入的Lambda表达式可以尾随到参数列表的小括号外部
}
我们查看一下Kotlin对应的Java代码:
从反编译得到的Java代码可以看出:编译器把两次调用这个函数的地方用这个函数的方法体进行了两次替换(图中的final修饰的main函数体),需要注意的是,内联函数提高代码性能的同时导致了代码量的增加,所以应该避免内联函数的函数体过大。
如果一个内联函数可以接收多个Lambda表达式作为参数,默认这些Lambda表达式都会被内联到调用处,如果需要某个Lambda表达式不被内联,这时候我们就需要针对某个Lambda表达式禁用内联,可以使用noinline关键字修饰对应的函数参数,示例如下:
//禁用内联
inline fun calculatePrintString(a: Int, b: Int, noinline message: () -> Unit, calc: (Int, Int) -> String) {
message()
println(calc(a, b))
}
fun main() {
calculatePrintString(3, 7, { println("开始计算") }, { a, b -> "$a + $b = ${a + b}" })
}
看一下反编译的Java代码:
其中message对应的Lambda表达式没有被内联,还是执行调用了。