kotlin - 内联函数

kotlin - 内联函数

首先我们了解一下什么是高阶函数:高阶函数就是为函数传入函数或者lambda表达式的函数。由于在我们执行传进来的函数或lambda表达式时都会为它们创建一个对象,在函数压找出栈的过程也是比较消耗性能的。所以为了避免这种函数的调用过程,编译器就会复制被调用的函数到原来的执行代码中。为了让告知编译器帮我们复制、粘贴哪些代码可以通过内联函数来实现。

内联函数的使用

只要用inline关键字修饰函数,那么这个函数就变成了内联函数。代码如下:

inline fun calculate(a: Int, b: Int, cal: (Int, Int) -> String) {
    println(cal(a, b))
}

fun main(args: Array<String>) {
    calculate(3, 4) { a, b ->
        "$a+$b=${a + b}"
    }
}

不加inline关键字反编译后对应的java代码如下:

public final class InlineFunDemoKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      calculate(3, 4, (Function2)null.INSTANCE);
   }

public static final void calculate(int a, int b, @NotNull Function2 cal) {
      Intrinsics.checkParameterIsNotNull(cal, "cal");
      Object var3 = cal.invoke(a, b);
      boolean var4 = false;
      System.out.println(var3);
   }
}

加了inline关键字反编译后对应的java代码如下:

public final class InlineFunDemoKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      byte a$iv = 3;
      int b$iv = 4;
      int $i$f$calculate = false;
      int var6 = false;
      String var5 = "" + a$iv + '+' + b$iv + '=' + (a$iv + b$iv);
      boolean var4 = false;
      System.out.println(var5);
   }

   public static final void calculate(int a, int b, @NotNull Function2 cal) {
      int $i$f$calculate = 0;
      Intrinsics.checkParameterIsNotNull(cal, "cal");
      Object var4 = cal.invoke(a, b);
      boolean var5 = false;
      System.out.println(var4);
   }
}

从上面这些代码我们很容看出来加了inline和不加inline关键字的区别,它会把inline函数的执行体拷贝到调用处。

部分禁止内联

在使用inline关键字后,这个函数和传入的函数或者lambda表达式都会被内联化,如果我们希望部分函数或lambda表达式不被内联化,可以使用noinline关键字来修饰该函数或者表达式。
例如如下代码:

fun main(args: Array<String>) {
    calculate(3, 4,{ println("开始计算")}) { a, b ->
        "$a+$b=${a + b}"
    }
}
inline fun calculate(a: Int, b: Int, noinline begin: () -> Unit, cal: (Int, Int) -> String) {
    begin()
    println(cal(a, b))
}

反编译后对应的java代码:

 public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      byte a$iv = 3;
      byte b$iv = 4;
      Function0 begin$iv = (Function0)null.INSTANCE;
      int $i$f$calculate = false;
      begin$iv.invoke();
      int var7 = false;
      String var6 = "" + a$iv + '+' + b$iv + '=' + (a$iv + b$iv);
      boolean var5 = false;
      System.out.println(var6);
   }

   public static final void calculate(int a, int b, @NotNull Function0 begin, @NotNull Function2 cal) {
      int $i$f$calculate = 0;
      Intrinsics.checkParameterIsNotNull(begin, "begin");
      Intrinsics.checkParameterIsNotNull(cal, "cal");
      begin.invoke();
      Object var5 = cal.invoke(a, b);·
      boolean var6 = false;
      System.out.println(var5);
   }

从上面的代码可以看出begin函数确实没有被内联化
注意:一个内联函数如果没有函数或者lambda表达式作为参数时,那么这个内联函数就没有多大意义,编译器会有警告信息。

什么情况下使用内联函数

函数或者lambda表达式的执行代码量不大时候就应该使用内联函数
如果函数或者lambda表达式代码量很大而且在多个地方调用那么就不适合用内联函数,因为有可能大量的复制代码比方法调用更影响性能。

非局部返回

  • 前面也提到默认情况下lambda表达式不允许直接写return的,这是因为如果是非内联lambda表达式,会在使用的时候额外的生成一个函数对象,所以这种表达式不可能用于返回它所在的函数。默认lambda表达式最后一条语句就是他的返回值。
  • 内联的lambda表达式的本质是代码的拷贝,所以它的内部可以写return返回的是它所在的函数。
    实例代码如下:
inline fun each(data:Array<Int>,fn:(Int)->Unit){
    for (el in data){
        fn(el)
    }
}
fun main(args: Array<String>) {
    val array = arrayOf(1, 2, 3)
    each(array){
        println(it)
        return //如each函数有inline修饰时,return返回的是main函数
                // 如果没有没有inline修饰编译会报'return' is not allowed here错误
    }
}

如果硬要在没有inline修饰的lambda表达始终写return,只能通过 return@方法名的语法不能单独使用return。

什么是非局部返回

就是在inline修饰的lambda中使用return返回的是它所在的函数而不是lambda函数本身的操作。

禁用非局部返回

通过内联函数可以使 Lambda表达式实现非局部返回,但是,如果一个内联函数的函数类型参数被crossinline修饰,则对应传入的 Lambda表达式将不能非局部返回了,只能局部返回了。还是用之前的例子修改:

inline fun each(data:Array<Int>,crossinline fn:(Int)->Unit){
    for (el in data){
        fn(el)
    }
}
fun main(args: Array<String>) {
    val array = arrayOf(1, 2, 3)
    each(array){
        println(it)
        return@each //如each函数有inline修饰时,return返回的是main函数
                // 如果没有没有inline修饰编译会报'return' is not allowed here错误
    }
}

上面的代码在函数类型参数上加了crossinline关键字,这是我们在传入的lambda表达式中就不能写return来非局部返回了,只能写return@each使其变为局部返回。

适用的场景

如果传入的lambda表达式不是直接在函数中调用的,而是在函数内部的局部函数或者匿名对象中调用的,而这个函数的后面还有别的逻辑这时候我们就要禁用非局部返回,否则这个函数在传入的函数执行之后的逻辑都无法执行了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值