Better

业精于勤荒于嬉

Idiomatic Kotlin: Inline functions

Better's Avatar 2019-06-21 Kotlin

  1. 1. Define And Use
    1. 1.1. lambda
    2. 1.2. function refrence
    3. 1.3. inline to inline
    4. 1.4. oninline
    5. 1.5. corssinline
  2. 2. 参考

高阶函数在编译后其实是用的匿名内部类的方式来实现,所以会有一点性能上的开销。使用 Inline 关键字来避免这种情形。被 Inline 关键字标记的高阶函数,在编译后是直接将高阶函数的函数体以及 Lambda 参数直接拷贝到调用处。这样就不会多创建匿名内部类或者方法引用实例,减少性能上的开销。

Define And Use

原则上任何函数都已加上inline,但是最好是加载高阶函数中。

1
Warning:(123, 4) Kotlin: Expected performance impact of inlining '...' can be insignificant. Inlining works best for functions with lambda parameters

lambda

不加inline 时:

1
2
3
4
5
6
7
8
9
fun notInlinedFilter(list: List<Int>, predicate: (Int) -> Boolean): List<Int> {
return list.filter(predicate)
}

fun notInlinedTest() {
val list = listOf(1, 2, 3)
val newList = notInlinedFilter(list) { it < 2 }
println(newList)
}

编译后predicate参数编译成一个Function1接口的实例

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
@NotNull
public static final List notInlinedFilter(@NotNull List list, @NotNull Function1 predicate) {
Intrinsics.checkParameterIsNotNull(list, "list");
Intrinsics.checkParameterIsNotNull(predicate, "predicate");
Iterable $receiver$iv = (Iterable)list;
Collection destination$iv$iv = (Collection)(new ArrayList());
Iterator var5 = $receiver$iv.iterator();

while(var5.hasNext()) {
Object element$iv$iv = var5.next();
if ((Boolean)predicate.invoke(element$iv$iv)) {
destination$iv$iv.add(element$iv$iv);
}
}

return (List)destination$iv$iv;
}

public static final void notInlinedTest() {
List list = CollectionsKt.listOf(new Integer[]{1, 2, 3});
List newList = notInlinedFilter(list, (Function1)new Function1<Integer, Boolean>() {
@Override
public Boolean invoke(Integer integer) {
return integer < 2;
}
});
System.out.println(newList);
}

使用inline定义

1
2
3
4
5
6
7
8
9
inline fun inlinedFilter(list : List<Int>, predicate : (Int) -> Boolean) : List<Int>{
return list.filter(predicate)
}

fun lambdaInCallSiteTest() {
val list = listOf(1,2,3)
val newList = inlinedFilter(list) {it < 2}
println(newList)
}

查看编译后的代码。

  • 申明处的函数inlinedFilter还是存在。所以Java 中的代码可以直接调用 Kotlin 中的 Inline 函数
  • 在调用处,inline 函数直接用它的函数替换了
  • 在调用处,lmabda参数predicate也被直接拷贝(翻译)过去了。在调用处直接被替代了
  • 调用处的的函数会被增大,行数变多。
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
public static final void lambdaInCallSiteTest() {
List list = CollectionsKt.listOf(new Integer[]{1, 2, 3});
Iterable $receiver$iv$iv = (Iterable)list;
Collection destination$iv$iv$iv = (Collection)(new ArrayList());
Iterator var5 = $receiver$iv$iv.iterator();

while(var5.hasNext()) {
Object element$iv$iv$iv = var5.next();
int it = ((Number)element$iv$iv$iv).intValue();
if (it < 2) {
destination$iv$iv$iv.add(element$iv$iv$iv);
}
}

List newList = (List)destination$iv$iv$iv;
System.out.println(newList);
}

@NotNull
public static final List inlinedFilter(@NotNull List list, @NotNull Function1 predicate) {
Intrinsics.checkParameterIsNotNull(list, "list");
Intrinsics.checkParameterIsNotNull(predicate, "predicate");
Iterable $receiver$iv = (Iterable)list;
Collection destination$iv$iv = (Collection)(new ArrayList());
Iterator var6 = $receiver$iv.iterator();

while(var6.hasNext()) {
Object element$iv$iv = var6.next();
if ((Boolean)predicate.invoke(element$iv$iv)) {
destination$iv$iv.add(element$iv$iv);
}
}

return (List)destination$iv$iv;
}

function refrence

1
2
3
4
5
6
7
8
9
10
11
inline fun inlinedFilter(list : List<Int>, predicate : (Int) -> Boolean) : List<Int>{
return list.filter(predicate)
}

fun filterLessThanTwo(input: Int) = input < 2

fun functionReferenceTest() {
val list = listOf(1,2,3)
val newList = inlinedFilter(list, ::filterLessThanTwo)
println(newList)
}

编译后的代码

  • 申明处的函数inlinedFilter还是存在。所以Java 中的代码可以直接调用 Kotlin 中的 Inline 函数
  • 在调用处,inline 函数直接用它的函数替换了
  • 在调用处,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
30
31
32
33
34
35
36
37
public static final boolean filterLessThanTwo(int input) {
return input < 2;
}

public static final void functionReferenceTest() {
List list = CollectionsKt.listOf(new Integer[]{1, 2, 3});
Iterable $receiver$iv$iv = (Iterable)list;
Collection destination$iv$iv$iv = (Collection)(new ArrayList());
Iterator var5 = $receiver$iv$iv.iterator();

while(var5.hasNext()) {
Object element$iv$iv$iv = var5.next();
int p1 = ((Number)element$iv$iv$iv).intValue();
if (filterLessThanTwo(p1)) {
destination$iv$iv$iv.add(element$iv$iv$iv);
}
}

List newList = (List)destination$iv$iv$iv;
System.out.println(newList);
}

@NotNull
public static final List inlinedFilter(@NotNull List list, @NotNull Function1 predicate) {
Intrinsics.checkParameterIsNotNull(list, "list");
Intrinsics.checkParameterIsNotNull(predicate, "predicate");
Iterable $receiver$iv = (Iterable)list;
Collection destination$iv$iv = (Collection)(new ArrayList());
Iterator var6 = $receiver$iv.iterator();

while(var6.hasNext()) {
Object element$iv$iv = var6.next();
if ((Boolean)predicate.invoke(element$iv$iv)) {
destination$iv$iv.add(element$iv$iv);
}
}
}

inline to inline

inline 函数的函数式类型参数是在调用处被转换,如果传入参数时已经是函数式类型的,则是转换不了的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
inline fun inlinedFilter(list : List<Int>, predicate : (Int) -> Boolean) : List<Int>{
return list.filter(predicate)
}

fun filterLessThanTwo(input: Int) = input < 2

fun lambdaInstance(predicate: (Int) -> Boolean) {
val list = listOf(1,2,3)
val newList = inlinedFilter(list, predicate)
println(newList)
}

fun lambdaInstanceTest() {
lambdaInstance(::filterLessThanTwo)
}

编译后的代码,在lambdaInstance()函数处的参数,还是是Function1类型的实例

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
36
37
38
39
40
41
42
43
44
45
46
47
48
public static final boolean filterLessThanTwo(int input) {
return input < 2;
}

public static final void lambdaInstanceTest() {
lambdaInstance((Function1) new Function1<Integer, Boolean>() {
@Override
public Boolean invoke(Integer integer) {
return filterLessThanTwo(integer);
}
});
}

public static final void lambdaInstance(@NotNull Function1 predicate) {
Intrinsics.checkParameterIsNotNull(predicate, "predicate");
List list = CollectionsKt.listOf(new Integer[]{1, 2, 3});
Iterable $receiver$iv$iv = (Iterable) list;
Collection destination$iv$iv$iv = (Collection) (new ArrayList());
Iterator var6 = $receiver$iv$iv.iterator();

while (var6.hasNext()) {
Object element$iv$iv$iv = var6.next();
if ((Boolean) predicate.invoke(element$iv$iv$iv)) {
destination$iv$iv$iv.add(element$iv$iv$iv);
}
}

List newList = (List) destination$iv$iv$iv;
System.out.println(newList);
}

@NotNull
public static final List inlinedFilter(@NotNull List list, @NotNull Function1 predicate) {
Intrinsics.checkParameterIsNotNull(list, "list");
Intrinsics.checkParameterIsNotNull(predicate, "predicate");
Iterable $receiver$iv = (Iterable) list;
Collection destination$iv$iv = (Collection) (new ArrayList());
Iterator var6 = $receiver$iv.iterator();

while (var6.hasNext()) {
Object element$iv$iv = var6.next();
if ((Boolean) predicate.invoke(element$iv$iv)) {
destination$iv$iv.add(element$iv$iv);
}
}

return (List) destination$iv$iv;
}

可以将lambdaInstance()定义为inline函数,这样在调用lambdaInstance()时的参数,就不会被编译成Function1的实例的。
inline函数的参数可以作为参数传递给另一个inline函数
inline函数参数不能被存储起来。因为编译后参数实际上是一段代码块,在真实的 Java 中是不能存储代码块到变量中的。

oninline

inline函数的函数式类型的参数是不能被存储在变量中的,使用noinline关键字标记这个函数式类型的参数后就可以被存储起来的。当然在调用处inline函数的函数体还是会直接替换,只不过参数不会直接替换了

corssinline

inliene的函数体内,如果也有 lambda 函数,在内嵌的 lambda 中是不能直接调用 inline 的函数式参数的。使用这个关键字规避

参考

Idiomatic Kotlin: Inline functions
functional_kotlin

This article was last updated on days ago, and the information described in the article may have changed.