kotlin 内联函数

112 阅读6分钟

Unit 相当于 java 中 void

1.kotlin 为啥整个内联函数?

在 kotlin 高阶函数的编译中:使用的 lambda 表达式在底层会被替换为 匿名类, 每次调用 lambda 表达式都会创建一个匿名类对象,增加内存和性能的开销。

fun num1AndNum2OnInline(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}

fun main() {
    num1AndNum2OnInline(2, 3) { a, b ->
        a + b
    }
}

kotlin 编译器在编译时会把高阶函数的lambda 表达式编译成内部类如下

public static int num1AndNum2OnInline(int num1,int num2,Function operation){
    int result = (int)operation.invoke(num1,num2)
    return result;
}

public static void main(){
    //lambda 表达式会被编译为 匿名类
    num1AndNum2OnInline(2, 3, new Function (){
        @Override
        public Integer invoke(int n1,int n1){
            return n1+n2;
        }
    });
}

2. 内涵函数

kotlin 通过内联函数解决lambda表达式创建匿名类的问题

2.1 inline 关键字

定义高阶函数时加上 inline 关键字即可。

//内联函数
//定义高阶函数时,加上 inline 关键字,就是一个内联函数
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int) :Int{
    return operation(num1,num2)
}

2.2 内联函数的作用

Kotlin 编译器会将内联函数中的代码在编译时自动替换到调用它位置,不在通过匿名类的方式实现

//内联函数
//定义高阶函数时,加上 inline 关键字,就是一个内联函数
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}

fun num1AndNum2NoInline(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}

fun main() {
    var result = num1AndNum2(2, 3) { a, b ->
        a + b
    }
}

在使用内联函数后,kotlin 编译器会将 内联函数的代码替换到调用的位置:如下

//编译后如下
fun main() {
    //将lambda 表达式中的代码替换到函数调用的位置
    var result = 2+3
}

2.3 内联函数和普通函数的区别

  • 内联函数所引用的 lambda 表达式中可以使用 return 进行函数返回;内联函数的lambda 可以直接使用 return
  • 普通函数引用的 lambda 表达式只能局部返回 只是返回 lambda 表达式;普通函数的lambda 表达式中不能直接 使用 return,可以使用 return@xxx实现局部返回

测试代码如下

fun main() {
    //普通函数和 内联函数 return 的区别
    noInlineFun("普通函数 的 return "){
        println("普通 lambda 函数体")
        // 只是 lambda 局部返回,
        return@noInlineFun // 普通函数
        println("普通 lambda 函数体 end ")
    }
    println("-----")
    inlineFun("内联函数 的 return "){
        println("内联 lambda 函数体")
        //调用 inlineFun()函数的 函数都返回了,后面的代码都不在执行
        return
    }
    println("Done")
}
fun noInlineFun(str:String ,block :(String)->Unit){
    println("noLineFun start str= $str")
    block(str)
    println("noLineFun end str= $str")
}
inline  fun  inlineFun(str:String ,block :(String)->Unit){
    println("inlineFun start str= $str")
    block(str)
    println("inlineFun end str= $str")
}

//注意 noLineFun end 输出了
noLineFun start str= 普通函数 的 return 
普通 lambda 函数体
noLineFun end str= 普通函数 的 return 
-----
inlineFun start str= 内联函数 的 return 
内联 lambda 函数体

Process finished with exit code 0

3.noinline

如果 内联函数有两个函数类型的入参,其中一个不想内联,可以使用 oninline 关键字修饰,取消内联。 代码如下:

inline fun moreParma(block: (String) -> Unit, noinline block1: (String) -> Unit) {
}
//FuncType 是先定义好的函数类型
inline fun moreParma(block: (String) -> Unit, noinline block1: FuncType) {
}

4.crossinline

使用场景:内联高阶函数中的函数类型参数,被用在lambda表达式中 如:

//内联函数 inlineFunCrossInline2() 中的函数类型参数 block 被用在 Runnable {} 中
inline fun inlineFunCrossInline2( crossinline block:()->String){
    var  run = Runnable { 
        block()
    }
}
//内联函数 inlineFunCrossInline() 中的函数类型参数 block 被用在 processFunction {} 中
 inline fun inlineFunCrossInline(str: String,  crossinline block: FuncType, block2: FuncType) {
   println(str)
    //processFunction 是一个普通的高阶函数函数,接收一个函数类型的参数
    var v = processFunction {
        var a = block(1)
        a
    }
     //inlineProcessFunction 是内联函数
    var v2 = inlineProcessFunction {
        var a = block2(1)
        a
    }
}
//processFunction 函数接受 FunType 类型的参数
fun processFunction(func: (Int) -> Int) {
    // 在这里可以使用传递进来的函数 func
    println(func(3))
}

普通的高阶函数中lambda表达式中不可以使用return,crossinline关键字像一个契约一样保证 lambda表达式中不会使用retrun。

上面的代码页证明了,如果是 内联函数 inlineProcessFunction 中调用 函数入参,则不需要 使用 crossinline 关键字。

5.定义函数类型

通过关键 typealias 声明一个 函数类型,在其他函数的入参中可以直接使用

//定义了FuncType 这样一个函数类型
typealias FuncType = (Int) -> Int

//processFunction 函数接受 FunType 类型的参数
fun processFunction(func: (Int) -> Int) {
    // 在这里可以使用传递进来的函数 func
    println(func(3))
}
//processFunction 函数接受 FunType 类型的参数
inline fun inlineProcessFunction(func: (Int) -> Int) {
    // 在这里可以使用传递进来的函数 func
    println(func(3))
}

fun myFunction(x: Int): Int {
    return x * 2
}

6.下面是 inline ,noinline ,reture ,crossinLine 完整代码

//内联函数
//定义高阶函数时,加上 inline 关键字,就是一个内联函数
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}
//普通高阶函数
fun num1AndNum2OnInline(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}

fun main() {
    var sum = num1AndNum2OnInline(2, 3) { a, b ->
        a + b
    }
    var sum2 = num1AndNum2(2, 3) { a, b ->
        a + b
    }
    //传入 函数类型
    processFunction(::myFunction)

    //crossInline
    inlineFunCrossInline("crossInline", { a ->
        var value = a * 2
        println("第一个lambda  =$value")
        value
    }) { a ->
        var value = a * 4
        println("第一个lambda =$value")
        value
    }
    //普通函数和 内联函数 return 的区别
    //普通函数 return
    noInlineFun("普通函数 的 return ") {
        println("普通 lambda 函数体")
        // 只是 noInlineFun() 返回,
        return@noInlineFun
        println("普通 lambda 函数体 end ")
    }
    println("-----")
    //内联函数
    inlineFun("内联函数 的 return ") {
        println("内联 lambda 函数体")
        //调用 inlineFun()函数的 函数都返回了,后面的代码都不在执行
        return
    }
    println("Done")

}
//定义了FuncType 这样一个函数类型
typealias FuncType = (Int) -> Int
//processFunction 函数接受 FunType 类型的参数
fun processFunction(func: (Int) -> Int) {
    // 在这里可以使用传递进来的函数 func
    println(func(3))
}
//processFunction 函数接受 FunType 类型的参数
inline fun inlineProcessFunction(func: (Int) -> Int) {
    // 在这里可以使用传递进来的函数 func
    println(func(3))
}
fun myFunction(x: Int): Int {
    return x * 2
}


fun noInlineFun(str: String, block: (String) -> Unit) {
    println("noLineFun start str= $str")
    block(str)
    println("noLineFun end str= $str")
}

inline fun inlineFun(str: String, block: (String) -> Unit) {
    println("inlineFun start str= $str")
    block(str)
    println("inlineFun end str= $str")
}

// 内联函数 多个函数入参 使用 noinline 取消内联
inline fun moreParma(block: (String) -> Unit, noinline block1: (String) -> Unit) {
}
//FuncType 是先定义好的函数类型
inline fun moreParma(block: (String) -> Unit, noinline block1: FuncType) {
}


inline fun inlineFunCrossInline(str: String, crossinline block: FuncType, block2: FuncType) {
    println(str)
    // processFunction 普通高阶函数
    var v = processFunction {
        var a = block(1)
        a
    }
    //inlineProcessFunction 是内联函数
    var v2 = inlineProcessFunction {
        var a = block2(1)
        a
    }
}

inline fun inlineFunCrossInline2(crossinline block: () -> String) {
    var run = Runnable {
        block()
    }
}