Kotlin-中的-Lambda-与-Inline,小白看完都学会了

2021年09月15日 阅读数:1
这篇文章主要向大家介绍Kotlin-中的-Lambda-与-Inline,小白看完都学会了,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。
 }

}编程


上面的代码咱们执行预期的输出应该是这样

higherOrderFunction.before
testLambdaReturn
higherOrderFunction.aftermarkdown


而后实际的执行结果却有点事与愿违

higherOrderFunction.before
testLambdaReturnide


缘由仍是发生了inline,higherOrderFunctionFirst的执行由于`runnable`中的return,形成了该方法的返回。

解决方法也比较简单,就是不直接使用return,而是使用指定label的return方式

fun testLambdaReturn() {
higherOrderFunctionFirst {
System.out.println("testLambdaReturn")
return@higherOrderFunctionFirst //valid
}函数

}性能


解决了上面的问题,咱们还须要带出一个技术概念,就是控制流。

## control flow 控制流

维基百科中的英文定义

> In computer science, control flow (or flow of control) is the order in which individual statements, instructions or function calls of an imperative program are executed or evaluated.

对应的中文意思是 在计算机科学中,控制流是单个语句(指令,或命令式编程中函数调用)的执行顺序。

## local control flow 本地控制流

本地控制流和上面的功能描述一致,只是限定了范围为方法内部。好比下面的代码

fun testControlFlow() {
functionA()
functionB()
functionC()
//..... other code
}优化


若是上面的方法遵循本地控制流,则`functionA`,`functionB`和`functionC`依次执行,可是存在违背本地控制流的状况,即非本地控制流(Non local control flow),常见的有

*   inline 的 Lambda 表达式含有return
*   上述的`functionA`,`functionB`或`functionC`发生[未捕获异常](

)
*   协程也会致使 Non local control flow出现

对于Lambda中的return,除了上述的解决方法,还有下面两种解决方法

## noinline

*   noinline 用来限定 lambda表达式
*   noinline 强制lambda表达式 不进行inline处理,对应的方式就是翻译成内部类实现。
*   noinline 须要配合inline使用

使用示例以下

inline fun bigHigherOrderFunction(firstRunnable: () -> Unit, noinline secondRunnable: () -> Unit, thirdRunnable: () -> Unit) {
firstRunnable()
secondRunnable()
thirdRunnable()
}lua

fun testNoInline() {
bigHigherOrderFunction({
System.out.println("firstRunnable")
}, {
System.out.println("secondRunnable")
//return //not allowed if the lambda is noinlined
}, {
System.out.println("thirdRunnable")
})
}翻译


反编译验证一下。

public final class NoinlineSampleKt {
public static final void bigHigherOrderFunction(@NotNull Function0 firstRunnable, @NotNull Function0 secondRunnable, @NotNull Function0 thirdRunnable) {
int $i$f$bigHigherOrderFunction = 0;
Intrinsics.checkParameterIsNotNull(firstRunnable, "firstRunnable");
Intrinsics.checkParameterIsNotNull(secondRunnable, "secondRunnable");
Intrinsics.checkParameterIsNotNull(thirdRunnable, "thirdRunnable");
firstRunnable.invoke();
secondRunnable.invoke();
thirdRunnable.invoke();
}code

public static final void testNoInline() {
Function0 secondRunnable$iv = (Function0)null.INSTANCE;
int $i$f$bigHigherOrderFunction = false;
int var2 = false;
System.out.println("firstRunnable");
secondRunnable$iv.invoke();
var2 = false;
System.out.println("thirdRunnable");
}协程

注意,对于不进行inline处理的 lambda 表达式中 不容许使用return。

## crossinline

可是咱们使用了上面的noinline,可能仍是担忧一些性能问题,好在这里,还有一个叫作crossinline的东西。

*   crossinline 须要配合inline一块儿起做用
*   crossinline 限定的 lambda 不容许使用return,避免了non local control flow问题

使用示例

//crossinline必须和inline结合使用
inline fun higherOrderFunctionCrossline(crossinline runnable: () -> Unit) {
runnable()
}

fun testCrossline() {
higherOrderFunctionCrossline {
System.out.println("testCrossline")
// return not allowed here
}
}


再次反编译验证代码

public static final void higherOrderFunctionCrossline(@NotNull Function0 runnable) {
int $i$f$higherOrderFunctionCrossline = 0;
Intrinsics.checkParameterIsNotNull(runnable, "runnable");
runnable.invoke();
}

public static final void testCrossline() {
int $i$f$higherOrderFunctionCrossline = false;
int var1 = false;
System.out.println("testCrossline");
}

## Kotlin inline与 JIT inline的区别

提到inline,据说过的朋友可能第一个想到的是 JIT 的 inline。JIT inline 是JVM虚拟机提供的运行时的一种优化方式。

来一段代码举例来讲

public int add(int x, int y) {
return x + y;
}

public void testAdd() {
//some code here
int result = add(a, b);
}

当JVM的JIT编译决定将add方法执行inline操做后,testAdd的方法实现会变成相似这样

public void testAdd() {
int result = a + b;
}

即 将add的方法体实现提取到调用处(testAdd方法中)。inline带来的好处也不言而喻,那就是减小了方法调用产生的进栈和出栈操做,提高运行时的效率。

**Kotlin的inline做用和JIT inline大致差很少,稍有一些不一样**

*   Kotlin的inline发生在编译时,而不是运行时
*   Kotlin的inline能够明确指定,而jit inline则没法指定发生。

## inline 带来的其余问题 can not access private variable

private val aPrivateValue = "A Private Value"

internal val internalValue = "Internal Value"

@PublishedApi
internal val taskId = "1"

val publicValue = ""

var publicVariable = ""

inline fun beToInlinedMethod(runnable: () -> Unit) {
//aPrivateValue //Public-API inline function cannot access non-public-API
// 'private val aPrivateValue: String' defined in root package in file InlineAccessPrivateMember.kt

// internalValue 一样也报错上面的错误

taskId

publicValue

publicVariable

}


上面的beToInlinedMethod 没法访问声明在同一文件中的`aPrivateValue`和`internalValue`,由于

*   `beToInlinedMethod`的方法体颇有可能被提取到别的模块的方法中