Kotlin之Lambda优化inline篇

问题来源

Kotlin每声明一个Lambda表达式,就会在字节码中产生一个匿名类,该匿名类包含了一个invoke方法,作为Lambda的调用方法,每次调用的时候,还会创建一个新对象。

解决方案

为了解决Lambda的开销问题。Kotlin引入了内联函数(inline),内联函数在编译期被嵌入每一个被调用的地方,以减少额外生成的匿名函数,以及函数执行的时间开销。

案例

未使用inline声明lambda

class LambdaDemo {

    fun main(args: Array<String>) {
        foo {
            print("哈哈哈哈哈哈")
        }
    }

    fun foo(block:()->Unit) {
        println("before block")
        block()
        println("after block")
    }

}

字节码

public final class LambdaDemo {
   public final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      this.foo((Function0)null.INSTANCE);
   }

   public final void foo(@NotNull Function0 block) {
      Intrinsics.checkParameterIsNotNull(block, "block");
      String var2 = "before block";
      boolean var3 = false;
      System.out.println(var2);
      block.invoke();
      var2 = "after block";
      var3 = false;
      System.out.println(var2);
   }
}

使用inline声明lambda

class LambdaDemo {

    fun main(args: Array<String>) {
        foo {
            print("哈哈哈哈哈哈")
        }
    }

    inline fun foo(block:()->Unit) {
        println("before block")
        block()
        println("after block")
    }

}

字节码

public final class LambdaDemo {
   public final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      int $i$f$foo = false;
      String var4 = "before block";
      boolean var5 = false;
      System.out.println(var4);
      int var6 = false;
      String var7 = "哈哈哈哈哈哈";
      boolean var8 = false;
      System.out.print(var7);
      var4 = "after block";
      var5 = false;
      System.out.println(var4);
   }

   public final void foo(@NotNull Function0 block) {
      int $i$f$foo = 0;
      Intrinsics.checkParameterIsNotNull(block, "block");
      String var3 = "before block";
      boolean var4 = false;
      System.out.println(var3);
      block.invoke();
      var3 = "after block";
      var4 = false;
      System.out.println(var3);
   }
}
注意
  1. 由于JVM对普通函数已经能够根据实际情况智能地判断是否进行内联优化,所以我们并不需要对其使用Kotlin的inline语法,那只会让字节码变得更加复杂。
  2. 尽量避免对具有大量函数体的函数进行内联,这样会导致过多的字节码数量。
  3. 一旦一个函数被定义未内联函数,便不能获取闭包类的私有成员,除非你把他们生命为internal。
  4. 上述例子中表明:如果在一个函数的开头加上一个inline修饰符,那么它的函数体及Lambda参数都会被内联。然而现实中的情况比较复杂,有一种可能是函数需要接收多个参数,但我们只想对其中部分Lambda参数内联,其他的则不内联,此时可以使用noinline关键字。
  5. 非局部返回:正常的Lambda表达式中不允许存在return关键字,如果声明Lambda表达式的时候加上inline关键字,可以达到非局部返回的效果(就是调用Lambda表达式的地方直接全局了,而不是局部返回)
  6. crollinline: 非局部返回可能存在危险,因为有时候,我们内联的函数所接收的Lambda参数常常来自于上下文其他地方。为了避免带有return的Lambda参数产生破坏,可以使用crossinline关键字来修饰该参数。
fun main(args: Array<String>) {
        foo {
            return // 此处报错,'return' is not allowed here
        }
    }

    inline fun foo(crossinline block:()->Unit) {
        println("before block")
        block()
        println("after block")
    }
  1. 让泛型不被擦除
    泛型在运行时会被擦除。在inline函数中我们可以指定类型不被擦除, 因为inline函数在编译期会将字节码copy到调用它的方法里,所以编译器会知道当前的方法中泛型对应的具体类型是什么,然后把泛型替换为具体类型,从而达到不被擦除的目的,在inline函数中我们可以通过reified关键字来标记这个泛型在编译时替换成具体类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值