inline
在编译时,会将此修饰符修饰的函数复制到调用处(称为内联),避免创建 Function 对象,以减少创建对象的内存开销。noinline
需要配合inline
使用,使用在函数型参上,告诉编译器当前这个函数不能内联crossinline
需要配合inline
使用,告诉编译器不能使用return
,否则就跳出调用处函数,但是可以使用return@label
跳出指定外层。(本身在lambda
内部就是不能使用return
的,而只能使用return@label
在 kotlin
中函数是一等公民,可以存储于变量和其他数据结构中,可以当作参数传递,也可以当做返回类型返回。
高阶函数:使用了函数参数或者返回函数类型的函数。
在编译时,每一个函数都会被编译为一个 FunctionX
类型,运行时,每一个函数都是一个对象。
为了优化此现象,可以使用 inline
修饰符,它会将函数的代码内联(复制)到调用处。
话语过于枯燥,实践出真知。
简单创建一个高阶函数,使用了两个函数型参。
fun cal(a: Int, b: Int, operate: (Int, Int) -> Int, result: (Int) -> Unit) {
var c = operate(a, b)
println("cal : $a ,$b = $c")
result(c)
}
调用处
fun test(): Int {
var product = 0
for (i in 1 until 10) {
cal(product, i, { a, b ->
var c = a + b
println("$a + $b = $c")
c
}, {
product += it
println("product -> $product")
})
println("for -> $i")
}
println(" 是不是到了这里,没有到这里,说明直接跳出 test 函数了 product ->$product")
return product
}
cal
函数使用了两个函数类型的参数,在调用处使用两个 lambda
表达式。
在编译时,每个 lambda
表达式都会被编译成 FunctionX
类,有几个参数编译成几,例如这里的 Function1
Function2
类(默认最多22个,再多就得自己写扩展了)。
在调用处就是创建对象使用了,看一下编译后的代码就很明确了。
public static final void cal(int a, int b, @NotNull Function2 operate, @NotNull Function1 result) {
Intrinsics.checkParameterIsNotNull(operate, "operate");
Intrinsics.checkParameterIsNotNull(result, "result");
int c = ((Number)operate.invoke(a, b)).intValue();
String var5 = "cal : " + a + " ," + b + " = " + c;
boolean var6 = false;
System.out.println(var5);
result.invoke(c);
}
public static final int test() {
final IntRef product = new IntRef();
product.element = 0;
int i = 1;
for(byte var2 = 10; i < var2; ++i) {
cal(product.element, i, (Function2)null.INSTANCE, (Function1)(new Function1() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
this.invoke(((Number)var1).intValue());
return Unit.INSTANCE;
}
public final void invoke(int it) {
IntRef var10000 = product;
var10000.element += it;
String var2 = "product -> " + product.element;
boolean var3 = false;
System.out.println(var2);
}
}));
String var3 = "for -> " + i;
boolean var4 = false;
System.out.println(var3);
}
String var5 = " 是不是到了这里,没有到这里,说明直接跳出 test 函数了 product ->" + product.element;
boolean var6 = false;
System.out.println(var5);
return product.element;
}
在使用 inline
修饰高阶函数后,就把代码内联到调用处了,就不会编译为 FunctionX
类,此举减少了运行时的对象创建开销。
inline fun cal(a: Int, b: Int, operate: (Int, Int) -> Int, result: (Int) -> Unit) {
var c = operate(a, b)
println("cal : $a ,$b = $c")
result(c)
}
看使用了 inline 后的调用处代码,是把代码复制过来了,不会再创建对象去使用了。虽然声明处并没有什么改变。
public static final void cal(int a, int b, @NotNull Function2 operate, @NotNull Function1 result) {
int $i$f$cal = 0;
Intrinsics.checkParameterIsNotNull(operate, "operate");
Intrinsics.checkParameterIsNotNull(result, "result");
int c = ((Number)operate.invoke(a, b)).intValue();
String var6 = "cal : " + a + " ," + b + " = " + c;
boolean var7 = false;
System.out.println(var6);
result.invoke(c);
}
public static final int test() {
int product = 0;
int i = 1;
for(byte var2 = 10; i < var2; ++i) {
int $i$f$cal = false;
int var7 = false;
int c = product + i;
String var9 = product + " + " + i + " = " + c;
boolean var10 = false;
System.out.println(var9);
String var6 = "cal : " + product + " ," + i + " = " + c;
boolean var5 = false;
System.out.println(var6);
int var15 = false;
product += c;
var9 = "product -> " + product;
var10 = false;
System.out.println(var9);
String var3 = "for -> " + i;
$i$f$cal = false;
System.out.println(var3);
}
String var12 = " 是不是到了这里,没有到这里,说明直接跳出 test 函数了 product ->" + product;
boolean var13 = false;
System.out.println(var12);
return product;
}
虽然这样避免了创建对象的内存开销,但是也出现了一个问题,那就是代码被内联到调用处了,lambda
内部就可以使用 return
语句了,并且会直接作用于调用处函数。
(本身 lambda
内部是不能使用 return
的)
fun test(): Int {
var product = 0
for (i in 1 until 10) {
cal(product, i, { a, b ->
var c = a + b
println("$a + $b = $c")
c
}, {
if (it > 50) { //如果使用了 inline 修饰 cal 函数会直接跳出 test 函数。因为在编译时已经被内联到这里了,所以 return 就是直接在 test() 函数里 return 了。如果不想这样,可以用 crossinline 修饰这个型参
println("$it > 50 了,就在这停了 ")
return 50
}
product += it
println("product -> $product")
})
println("for -> $i")
}
println(" 是不是到了这里,没有到这里,说明直接跳出 test 函数了 product ->$product")
return product
}
这里 就可以使用 crossinline
了。它需要配合 inline
使用,就是告诉编译器,不能使用 return
,还是得使用 return@label
来返回到指定层面。
inline fun cal(a: Int, b: Int, operate: (Int, Int) -> Int,crossinline result: (Int) -> Unit) {
var c = operate(a, b)
println("cal : $a ,$b = $c")
result(c)
}
fun test(): Int {
var product = 0
for (i in 1 until 10) {
cal(product, i, { a, b ->
var c = a + b
println("$a + $b = $c")
c
}, {
if (it > 50) { //如果使用了 inline 修饰 cal 函数会直接跳出 test 函数。因为在编译时已经被内联到这里了,所以 return 就是直接在 test() 函数里 return 了。如果不想这样,可以用 crossinline 修饰这个型参
println("$it > 50 了,就在这停了 ")
return@cal
}
product += it
println("product -> $product")
})
println("for -> $i")
}
println(" 是不是到了这里,没有到这里,说明直接跳出 test 函数了 product ->$product")
return product
}
编译后如下,已经很清晰了,return@label
语句被编译为了 if
控制语句,并不会跳出 test()
方法
public static final int test() {
int product = 0;
int i = 1;
for(byte var2 = 10; i < var2; ++i) {
int $i$f$cal = false;
int var7 = false;
int c = product + i;
String var9 = product + " + " + i + " = " + c;
boolean var10 = false;
System.out.println(var9);
String var6 = "cal : " + product + " ," + i + " = " + c;
boolean var5 = false;
System.out.println(var6);
int var15 = false;
if (c > 50) {
var9 = c + " > 50 了,就在这停了 ";
var10 = false;
System.out.println(var9);
} else {
product += c;
var9 = "product -> " + product;
var10 = false;
System.out.println(var9);
}
String var3 = "for -> " + i;
$i$f$cal = false;
System.out.println(var3);
}
String var12 = " 是不是到了这里,没有到这里,说明直接跳出 test 函数了 product ->" + product;
boolean var13 = false;
System.out.println(var12);
return product;
}
有时候并不想全部的函数型参都被内联,怎么办呢,noinline
就上场了。
它会告诉编译器,这个参数不能内联。
inline fun cal(a: Int, b: Int,noinline operate: (Int, Int) -> Int,crossinline result: (Int) -> Unit) {
var c = operate(a, b)
println("cal : $a ,$b = $c")
result(c)
}
调用处代码编译后如下,很清晰的看到又创建了 Function2
对象。这个参数并没有内联。
public static final int test() {
int product = 0;
int i = 1;
for(byte var2 = 10; i < var2; ++i) {
Function2 operate$iv = (Function2)null.INSTANCE;
int $i$f$cal = false;
int c$iv = ((Number)operate$iv.invoke(product, i)).intValue();
String var7 = "cal : " + product + " ," + i + " = " + c$iv;
boolean var8 = false;
System.out.println(var7);
int var10 = false;
String var11;
boolean var12;
if (c$iv > 50) {
var11 = c$iv + " > 50 了,就在这停了 ";
var12 = false;
System.out.println(var11);
} else {
product += c$iv;
var11 = "product -> " + product;
var12 = false;
System.out.println(var11);
}
String var3 = "for -> " + i;
boolean var15 = false;
System.out.println(var3);
}
String var13 = " 是不是到了这里,没有到这里,说明直接跳出 test 函数了 product ->" + product;
boolean var14 = false;
System.out.println(var13);
return product;
}
最后附上标准库 Functions.kt 的代码,其实每个 lambda 都会被编译成对应的类型,目前默认是最多 22个参数。 我使用的版本 1.4.0
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// Auto-generated file. DO NOT EDIT!
package kotlin.jvm.functions
/** A function that takes 0 arguments. */
public interface Function0<out R> : Function<R> {
/** Invokes the function. */
public operator fun invoke(): R
}
/** A function that takes 1 argument. */
public interface Function1<in P1, out R> : Function<R> {
/** Invokes the function with the specified argument. */
public operator fun invoke(p1: P1): R
}
/** A function that takes 2 arguments. */
public interface Function2<in P1, in P2, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2): R
}
/** A function that takes 3 arguments. */
public interface Function3<in P1, in P2, in P3, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3): R
}
/** A function that takes 4 arguments. */
public interface Function4<in P1, in P2, in P3, in P4, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4): R
}
/** A function that takes 5 arguments. */
public interface Function5<in P1, in P2, in P3, in P4, in P5, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5): R
}
/** A function that takes 6 arguments. */
public interface Function6<in P1, in P2, in P3, in P4, in P5, in P6, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6): R
}
/** A function that takes 7 arguments. */
public interface Function7<in P1, in P2, in P3, in P4, in P5, in P6, in P7, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7): R
}
/** A function that takes 8 arguments. */
public interface Function8<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8): R
}
/** A function that takes 9 arguments. */
public interface Function9<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9): R
}
/** A function that takes 10 arguments. */
public interface Function10<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10): R
}
/** A function that takes 11 arguments. */
public interface Function11<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11): R
}
/** A function that takes 12 arguments. */
public interface Function12<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12): R
}
/** A function that takes 13 arguments. */
public interface Function13<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13): R
}
/** A function that takes 14 arguments. */
public interface Function14<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14): R
}
/** A function that takes 15 arguments. */
public interface Function15<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15): R
}
/** A function that takes 16 arguments. */
public interface Function16<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16): R
}
/** A function that takes 17 arguments. */
public interface Function17<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17): R
}
/** A function that takes 18 arguments. */
public interface Function18<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18): R
}
/** A function that takes 19 arguments. */
public interface Function19<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19): R
}
/** A function that takes 20 arguments. */
public interface Function20<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20): R
}
/** A function that takes 21 arguments. */
public interface Function21<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21): R
}
/** A function that takes 22 arguments. */
public interface Function22<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
}