kotlin 语法进阶 - 内联函数inline 、noinline 、crossinline的作用

内联函数

使用inline关键字修饰的函数称为内联函数
内联函数主要用于减少高阶函数的使用开销
我们在使用高阶函数的时候,我们传入的lambda都会被编译成一个Function对象,这个过程会带来一定的性能开销。
如下代码:

inline fun calculate(a: Int, b: Int, block: (Int, Int) -> Int): Int {
    
    
    Log.e("calculate", "执行了计算")
    return block(a, b)
}
val result = calculate(1, 2) {
    
     a, b ->
    a + b
}

我们通过编译器将其转换为Java代码如下:

public final int calculate(int a, int b, @NotNull Function2 block) {
    
    
   Intrinsics.checkNotNullParameter(block, "block");
   Log.e("calculate", "执行了计算");
   return ((Number)block.invoke(a, b)).intValue();
}
int result = this.calculate(1, 2, (Function2)null.INSTANCE);

可以看到我们的lambda表达式被转换成了一个Function对象,还调用了这个Function对象的invoke方法。每次调用都会创建一个新对象,如果放在循环中使用,情况更糟糕,为了减少这种开销,我们使用内联函数。
使用关键字 inline 来表示内联函数。其作用就是:在编译时期,把调用这个函数的地方用这个函数的方法体进行替换。

我们将上面的代码改为内联函数,如下:

inline fun calculate(a: Int, b: Int, block: (Int, Int) -> Int): Int {
    
    
    Log.e("calculate", "执行了计算")
    return block(a, b)
}

我们再次通过编译器将其转换为Java代码如下:

public final int calculate(int a, int b, @NotNull Function2 block) {
    
    
   Intrinsics.checkNotNullParameter(block, "block");
   int $i$f$calculate = false;
   Log.e("calculate", "执行了计算");
   return ((Number)block.invoke(a, b)).intValue();
}
byte a$iv = 1;
int b$iv = 2;
int $i$f$calculate = false;
Log.e("calculate", "执行了计算");
int var9 = false;
int var10000 = a$iv + b$iv;

我们看到并没有创建Function对象,并且连calculate方法都没有调用,而是在应该调用calculate方法的地方,直接使用了calculate方法的代码逻辑。

禁止内联noinline

如果一个内联函数中由多个lambda表达式参数,我们可以使用noinline 前缀关键字表示不参加内联的参数。inline虽然会优化高阶函数的性能,但也会带来问题:会导致函数中的函数类型的参数无法被当做对象使用,错误使用inline编译器会提示报错,我们要做的就是添加noinline即可。
如下:我们需要返回的是一个函数类型的对象,但我们前面看到内联会将对象替换为代码逻辑,那我们就无法获取到一个对象,这时候编译器会报错提示我们

inline fun progress(text:String, block: () -> Unit): () ->Unit {
    
    
    Log.e("tag","运行")
   return block
}

为此我们给block添加noinline禁止内联,block依旧会被编译成一个对象,代码正常运行,当然因为我们的方法中只有一个函数类型的参数,此时inline没用可以省略掉了,如下:

 fun progress(text:String, noinline block: () -> Unit): () ->Unit {
    
    
    Log.e("tag","运行")
   return block
}

如果有两个函数类型的参数,一个内联,一个不内联,inline就不能省略,如下:

inline fun progress(text:String, block0: () -> Unit,noinline block: () -> Unit): () ->Unit {
    
    
    Log.e("tag","运行")
   return block
}

所以内联的使用规范:高阶函数一般都加上inline 进行内联以提高性能,如果报错就给函数类型的参数添加noinline禁止内联。

crossinline 处理内联带来的流程控制问题

如下:我们在内联的lambda表达式中加入了一个流程控制return,内联会把内联函数的代码逻辑拷贝到调用处,那么这个return也被拷贝过去了,所以我们调用 printProcess() 方法只会打印开始、过程,而不会打印结束,这显然是我们不愿意看到的,为了避免这种情况,我们使用crossinline前缀关键字

fun printProcess() {
    
    
    Log.e("printProcess", "开始")
    print {
    
    
        Log.e("printProcess", "过程")
        return
    }
    Log.e("printProcess", "结束")
}

inline fun print(block: () -> Unit) {
    
    
    block()
}

修改如下:我们使用crossinline关键字后,编译器会报错,告诉我们不能return,但我们可以 return@print 进行局部返回,所以crossinline关键字的作用就是避免非局部返回。

fun printProcess() {
    
    
    Log.e("printProcess", "开始")
    print {
    
    
        Log.e("printProcess", "过程")
        return@print
    }
    Log.e("printProcess", "结束")
}

inline fun print(crossinline block: () -> Unit) {
    
    
    block()
}

猜你喜欢

转载自blog.csdn.net/weixin_43864176/article/details/128594513