天天看点

Kotlin 中 Lambda的使用 四、内联函数 inline

一、内联函数

        用关键词inline修饰的函数,称为内联函数。inline修饰的是用函数作为参数的高阶函数,如果修饰普通函数为警告  Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types,普通函数内联对性能的影响微不足道。

二、用inline修饰高阶函数的好处

        我们在kotlin中对高阶函数分别使用普通函数声明和使用内联函数声明然后转成java代码然后进行比较。kotlin代码:

class Test {
    //不使用inline 修饰的高阶函数
    fun test(f: () -> Unit) {
        f()
    }
    //使用inline 修饰的高阶函数
    inline fun testInline(f: () -> Unit) {
        f()
    }
    fun call() {
        test { print("test") }
        testInline { print("testInline") }
    }
}
           

java代码:

public final class Test {
   public final void test(@NotNull Function0 f) {
      Intrinsics.checkNotNullParameter(f, "f");
      f.invoke();
   }

   public final void testInline(@NotNull Function0 f) {
      int $i$f$testInline = 0;
      Intrinsics.checkNotNullParameter(f, "f");
      f.invoke();
   }

   public final void call() {
      this.test((Function0)null.INSTANCE);
      int $i$f$testInline = false;
      int var3 = false;
      String var4 = "testInline";
      boolean var5 = false;
      System.out.print(var4);
   }
}
           

        在call中可以看出在调用非内联函数时是直接调用了此函数,并且创建了匿名类Function0用于Lambda函数的调用。内联函数则是复制了函数体过来,而没有创建匿名类,而是直接嵌入了

Lambda

函数的实现体。

        当高阶函数没有使用

Inline

修饰时,调用此函数会直接引用此函数,并且会创建匿名类以实现此函数参数的调用,这有两部分开销,直接调用此函数会创建额外的栈帧以及入栈出栈操作(一个函数的调用就是一个栈帧入栈和出栈的过程),并且匿名类的创建也会消耗性能。使用 Inline 修饰高阶函数是会有性能上的提升

         在调用内联函数时,基于上述我们已经知道内联会将

Lambda

参数函数体直接进行嵌入,内联可能导致生成的代码增加;不过如果我们使用得当(即避免内联过大函数),性能上会有所提升,尤其是在循环中的“超多态(megamorphic)”调用处。

三、禁用内联 noinline

        如果希望只内联一部分传给内联函数的 lambda 表达式参数,那么可以用 

noinline

 修饰符标记不希望内联的函数参数:

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { //…… }
           

        可以内联的 lambda 表达式只能在内联函数内部调用或者作为可内联函数的参数传递。当作为非内联函数的参数传递或者存储到字段中的时候需要noinline修饰。

inline fun testInline(f1: () -> Unit,  f: () -> Unit) {
    f1.invoke()
    val a = f//会提示错误,内联Lambad 不能参数赋值
    testInlineInner(f) //会提示错误,内联Lambad 不能作为普通函数参数进行传递
}

fun testInlineInner(f: () -> Unit) {
    f()
}
           
inline fun testInline(f1: () -> Unit,  noinline f: () -> Unit) {
    f1.invoke()
    val a = f//可以
    testInlineInner(f) //可以
}
           

四、在内联函数中的lambda表达式中使用return(非局部返回)

        在 Kotlin 中,我们只能对具名或匿名函数使用正常的、非限定的 return 来退出。 退出一个 lambda 表达式,我们必须使用一个标签, lambda 表达式内部禁止使用裸 

return

,因为 lambda 表达式不能使包含它的函数返回。

fun testInlineInner(f: () -> Unit) {
    f()
}
testInlineInner {
        return//'return' is not allowed here
    }
           

但是如果 lambda 表达式传给的函数是内联的,该 return 也可以内联,所以它是允许的 ,这种返回(位于 lambda 表达式中,但退出包含它的函数)称为非局部返回。

inline fun testInlineInner(f: () -> Unit) {
    f()
}

fun main(args: Array<String>) {
    testInlineInner {
        return// 可以,退出main函数
    }
}
           

五、crossinline

        当内联

Lambda

不是直接在函数体中调用,而是在嵌套函数或者其他执行环境中调用则需要声明为

crossinline。

inline fun f(crossinline body: () -> Unit) {
   val f = Runnable { body() }
}
           

六、

Reified 类型的参数

        reified 是Kotlin关于泛型的关键字,从而达到泛型不被擦除,如果需要使用reified 去修饰泛型方法中的泛型类型,则需要使用Inline修饰此泛型方法,因为Inline函数可以指定泛型类型不被擦除,因为内联函数编译期会将字节码嵌入到调用它的地方,所以编译器才会知道泛型对应的具体类型,使用reified 和 Inline适用于泛型方法,并且方法体中需要对泛型类型做以判断的情况。​​​​​​​

inline fun <reified T> Bundle.plus(key: String, value: T) {
    when (value) {
        is String -> putString(key, value)
        is Long -> putLong(key, value)
        is Int -> putInt(key, value)
    }
}
           

七、内联属性

inline

也可以修饰

set

get

方法,并且可以直接修饰属性使两个访问器都标记为

inline。

class Amount(var amount: Long) {
    private val isEmpty: Boolean
        inline get() {
            return amount <= 0
        }

    private val isEmptyNoInline: Boolean
        get() {
            return amount <= 0
        }

    fun test(){
       val amount = Amount(10)
        val isEmpty = amount.isEmpty
        val isEmptyNoInline = amount.isEmptyNoInline
    }
    //转化为Java代码
    public final void test() { 
      Amount amount = new Amount(10L);
      int $i$f$isEmpty = false;
      boolean isEmpty = amount.getAmount() <= 0L; //代码嵌入
      boolean isEmptyNoInline = amount.isEmptyNoInline(); //非嵌入
   }
}
           

一、入门Lambda表达式在集合函数式API的使用

二、函数类型和实例化

三、SAM转换 (Single Abstract Method Conversions)

四、内联函数 inline​​​​​​​

继续阅读