kotlin学习 - Kotlin inline noinline crossinline

kotlin学习 - Kotlin inline noinline crossinline

  • inline: 声明在编译时,将函数的代码拷贝到调用的地方(内联)
  • oninline: 声明 inline 函数的形参中,不希望内联的 lambda
  • crossinline: 表明 inline 函数的形参中的 lambda 不能有 return

inline 内联

使用 inline 声明的函数,在编译时将会拷贝到调用的地方。如果一个函数是 inline 的,那么编译器会在编译的时候,把这个函数复制到调用处。

优势:

  1. 减少函数调用的次数。虽然函数调用的开销很小,但是确实是有一定的开销的。尤其是在大量的循环中,这种开销会变得更加明显。

  2. 减少对象的生成。当方法中,有一个参数是 lambda 的时候,使用 inline 的方法,可以减少对象的生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Kotlin

fun main(args: Array<String>) {
for (i in 0..10) {
sum(1, 2) { println("Result is: $it") }
}
}

inline fun sum(a: Int, b: Int, lambda: (result: Int) -> Unit): Int {
val r = a + b
lambda.invoke(r)
return r
}

//反编译为 Java

public static final void main(@NotNull String[] args) {
//...
int var1 = 0;

for(byte var2 = 10; var1 <= var2; ++var1) {
byte a$iv = 1;
int b$iv = 2;
int r$iv = a$iv + b$iv;
String var9 = "Result is: " + r$iv;
System.out.println(var9);
}
}

noinline

noinline 修饰的是 inline 方法中的 lambda 参数。当一个 inline 函数中,有多个 lambda 作为参数时,可以在不想内联的 lambda 前使用 noinline 声明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//Kotlin

inline fun sum(a: Int, b: Int, lambda: (result: Int) -> Unit, noinline lambda2: (result: Int) -> Unit): Int {
val r = a + b
lambda.invoke(r)
lambda2.invoke(r)
return r
}

fun main(args: Array<String>) {
sum(1, 2,
{ println("Result is: $it") },
{ println("Invoke lambda2: $it") }
)
}

//反编译为 Java

public static final int sum(int a, int b, @NotNull Function1 lambda, @NotNull Function1 lambda2) {
int r = a + b;
lambda.invoke(r);
lambda2.invoke(r);
return r;
}

public static final void main(@NotNull String[] args) {
byte a$iv = 1;
byte b$iv = 2;
Function1 lambda2$iv = (Function1)null.INSTANCE;
int r$iv = a$iv + b$iv;
String var8 = "Result is: " + r$iv;
System.out.println(var8);
lambda2$iv.invoke(r$iv);
}

crossinline

声明一个 lambda 不能有 return 语句(可以有 return@label 语句)。这样可以避免使用 inline 时,lambda 中的 return 影响程序流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
inline fun sum(a: Int, b: Int, crossinline lambda: (result: Int) -> Unit): Int {
val r = a + b
lambda.invoke(r)
return r
}

fun main(args: Array<String>) {
sum(1, 2) {
println("Result is: $it")
return // 编译错误: return is not allowed here
}
}

总结

  • 使用 inline,内联函数到调用的地方,能减少函数调用造成的额外开销,在循环中尤其有效
  • 使用 inline 能避免函数的 lambda 形参额外创建 Function 对象
  • 使用 noinline 可以拒绝形参 lambda 内联
  • 使用 crossinline 显示声明 inline 函数的形参 lambda 不能有 return 语句,避免lambda 中的 return 影响外部程序流程