高阶函数实现原理

由于 Kotlin 兼容 Java 1.6,因此 JVM 是不懂什么是高阶函数的,我们的高阶函数最终一定会被编译器转换成 JVM 能够理解的格式。

1
2
3
4
5
6
7
8
9
10
11
12
// HigherOrderExample.kt

fun foo(block: () -> Unit) {
block()
}

fun main() {
var i = 0
foo{
i++
}
}

反编译成 Java 后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class HigherOrderExampleKt {
public static final void foo(Function0 block) {
block.invoke();
}

public static final void main() {
int i = 0
foo((Function0)(new Function0() {
public final void invoke() {
i++;
}
}));
}
}

Kotlin 高阶函数当中的函数类型参数,变成了 Function0,而 main() 函数当中的高阶函数调用,也变成了“匿名内部类”的调用方式。

1
2
3
public interface Function0<out R> : Function<R> {
public operator fun invoke(): R
}

inline原理

使用 inline 优化过的高阶函数会是什么样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// HigherOrderInlineExample.kt
/*
多了一个关键字
↓ */
inline fun fooInline(block: () -> Unit) {
block()
}

fun main() {
var i = 0
fooInline{
i++
}
}

反编译后的 Java

1
2
3
4
5
6
7
8
9
10
11
12
public final class HigherOrderInlineExampleKt {
// 没有变化
public static final void fooInline(Function0 block) {
block.invoke();
}

public static final void main() {
// 差别在这里
int i = 0;
int i = i + 1;
}
}


inline 的作用其实就是将 inline 函数当中的代码拷贝到调用处。

是否使用 inline,main() 函数会有以下两个区别:

  • 在不使用 inline 的情况下,我们的 main() 方法当中,需要调用 foo() 这个函数,这里多了一次函数调用的开销。
  • 在不使用 inline 的情况下,调用 foo() 函数时,还创建了“Function0”的匿名内部类对象,这也是额外的开销。

inline 的局限性

Kotlin 官方只建议我们将 inline 用于修饰高阶函数。对于普通的 Kotlin 函数,如果我们用 inline 去修饰它,IntelliJ 会对我们发出警告。

inline 的作用其实就是将 inline 函数当中的代码拷贝到调用处。由于 processText() 是公开的,因此它会从外部被调用,这意味着它的代码会被拷贝到外部去执行,而 getWordCount() 和 mapToList() 这两个函数却无法在外部被访问。这就是导致编译器报错的原因。

关注我