内容简介:Kotlin 针对函数提供了几个关键字 inline noinline crossinline,其涉及 Kotlin 中内联函数和使用定义一个
Kotlin 针对函数提供了几个关键字 inline noinline crossinline,其涉及 Kotlin 中内联函数和 lambda
相关的问题。
概览
-
inline
: 声明在编译时,将函数的代码拷贝到调用的地方(内联) -
oninline
: 声明inline
函数的形参中,不希望内联的lambda
-
crossinline
: 表明inline
函数的形参中的lambda
不能有return
inline
使用 inline
声明的函数,在编译时将会拷贝到调用的地方。
inline function
定义一个 sum
函数计算两个数的和。
fun main(args: Array<String>) { println(sum(1, 2)) } fun sum(a: Int, b: Int): Int { return a + b } 复制代码
反编译为 Java 代码:
public static final void main(@NotNull String[] args) { int var1 = sum(1, 2); System.out.println(var1); } public static final int sum(int a, int b) { return a + b; } 复制代码
正常的样子,在该调用的地方调用函数。
然后为 sum
函数添加 inline
声明:
inline fun sum(a: Int, b: Int): Int { return a + b } 复制代码
再反编译为 Java 代码:
public static final void main(@NotNull String[] args) { //... byte a$iv = 1; int b$iv = 2; int var4 = a$iv + b$iv; System.out.println(var4); } public static final int sum(int a, int b) { return a + b; } 复制代码
sum
函数的实现代码被直接拷贝到了调用的地方。
上面两个使用实例并没有体现出 inline
的优势。当你的函数中有 lambda
形参时, inline
的优势才会体现。
inline function with lambda parameters
考虑如下代码,会被编译成怎样的 Java 代码?
fun sum(a: Int, b: Int, 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") } } 复制代码
反编译为 Java:
public static final int sum(int a, int b, @NotNull Function1 lambda) { //... int r = a + b; lambda.invoke(r); return r; } public static final void main(@NotNull String[] args) { //... sum(1, 2, (Function1)null.INSTANCE); } 复制代码
(Function1)null.INSTANCE
,是由于反编译器 工具 在找不到等效的 Java 类时的显示的结果。
我传递的那个 lambda
被转换为 Function1
类型,它是 Kotlin 函数(kotlin.jvm.functions包)的一部分,它以 1 结尾是因为我们在 lambda
函数中传递了一个参数( result:Int
)。
再考虑如下代码:
fun main(args: Array<String>) { for (i in 0..10) { sum(1, 2) { println("Result is: $it") } } } 复制代码
我在循环中调用 sum
函数,每次传递一个 lambda
打印结果。反编译为 Java:
for(byte var2 = 10; var1 <= var2; ++var1) { sum(1, 2, (Function1)null.INSTANCE); } 复制代码
可见在每次循环里都会创建一个 Function1
的实例对象。这里就是性能的优化点所在,如何避免在循环里创建新的对象?
-
在循环外部创建
lambda
对象
val l: (r: Int) -> Unit = { println(it) } for (i in 0..10) { sum(1, 2, l) } 复制代码
反编译为 Java:
Function1 l = (Function1)null.INSTANCE; int var2 = 0; for(byte var3 = 10; var2 <= var3; ++var2) { sum(1, 2, l); } 复制代码
只会创建一个 Function
对象
-
使用
inline
:
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); } } 复制代码
lambda
代码在编译时被拷贝到调用的地方, 避免了创建 Function
对象。
inline 注意事项
public inline 函数不能访问私有属性
class Demo(private val title: String) { inline fun test(l: () -> Unit) { println("Title: $title") // 编译错误: Public-Api inline function cannot access non-Public-Api prive final val title } // 私有的没问题 private inline fun test(l: () -> Unit) { println("Title: $title") } } 复制代码
注意程序控制流
当使用 inline
时,如果传递给 inline
函数的 lambda
,有 return
语句,那么会导致闭包的调用者也返回。
例子:
inline fun sum(a: Int, b: Int, lambda: (result: Int) -> Unit): Int { val r = a + b lambda.invoke(r) return r } fun main(args: Array<String>) { println("Start") sum(1, 2) { println("Result is: $it") return // 这个会导致 main 函数 return } println("Done") } 复制代码
反编译 Java:
public static final void main(@NotNull String[] args) { String var1 = "Start"; System.out.println(var1); byte a$iv = 1; int b$iv = 2; int r$iv = a$iv + b$iv; String var7 = "Result is: " + r$iv; System.out.println(var7); } 复制代码
反编译之后也能看到, lambda
return
之后的代码不会执行。
如何避免?
可以使用 return@label
语法,返回到 lambda
被调用的地方。
fun main(args: Array<String>) { println("Start") sum(1, 2) { println("Result is: $it") return@sum } println("Done") } 复制代码
noinline
当一个 inline
函数中,有多个 lambda
作为参数时,可以在不想内联的 lambda
前使用 noinline
声明.
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); } 复制代码
第一个 lambda
内联到了调用处,而第二个使用 noinline
声明的 lambda
没有。
crossinline
声明一个 lambda
不能有 return
语句(可以有 return@label
语句)。这样可以避免使用 inline
时, lambda
中的 return
影响程序流程。
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 } } 复制代码
以上所述就是小编给大家介绍的《Kotlin inline noinline crossinline 解答》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
应用密码学:协议、算法与C源程序(原书第2版)
(美)Bruce Schneier / 吴世忠、祝世雄、张文政 等 / 机械工业出版社 / 2014-1 / 79.00
......我所读过的关于密码学最好的书......该书是美国国家安全局最不愿意见到出版的书...... —— 《Wired》 ......不朽的......令人着迷的......计算机程序员必读的密码学上决定性的著作...... —— 《Dr.Dobb's Journal》 ......该领域勿庸置疑的一本权威之作。 —— 《PC Magazine》 ..........一起来看看 《应用密码学:协议、算法与C源程序(原书第2版)》 这本书的介绍吧!