Kotlin内联函数关键字inline、noinline和crossinline
你在网上查这个东西,得到的结果肯定是这样:
inline: 函数的代码内联到调用处noinline:inline函数的形参中,不希望内联的lambda,此时这个lambda里不能returncrossinline:inline函数的形参中的lambda不能有return
你这乍一看,noinline和crossinline好像根本没毛线区别
但事实不是这样的
inline做了些啥⌗
inline可以将代码块内联,同时导致一个结果,你可以在这个内联的代码块内部,直接返回调用此代码块的函数,例如:
inline fun testInline(block: () -> Unit) {
block()
println("Hey! Don't pass me!")
}
fun main() {
testInline {
println("I'm the evil block")
// This will pass the println() call in testInline()
return
}
}
// 上面的代码内联后:
fun main() {
println("I'm the evil block")
return
// Cannot reach this line
println("Hey! Don't pass me!")
}
这导致了一个问题,在block()执行后,可能无法完成inline函数中的剩余过程,这在很多Builder模式的lambda中是非常致命的,此时,就需要禁用这种非局部返回,就需要使用到crossinline
crossinline⌗
使用crossinline修饰lambda,就可以阻止lambda内部的return,return只可以返回到lambda的作用域内
inline fun testInline(crossinline block: () -> Unit) {
block()
println("You can't pass me again!")
}
fun main() {
testInline {
println("HAHAHAHAHAHA!")
// 'return' is not allowed here
return
// return@testInline is allowed
}
}
noinline⌗
再说说noinline,直译这个关键字也能看得挺明白了,就是不让你inline,用原来的方式传递lambda参数。
那什么场景下需要noinline呢?一般就是你需要一个lambda的引用的时候,例如:
class Timer(
val interval: Long,
val callback: () -> Unit
) {
/* other logic that may call tick() */
/* invoke callback */
fun tick() = callback()
}
inline fun makeTimer(
interval: () -> Long,
noinline callback: () -> Unit
) = Timer(interval(), callback)
fun main() {
val timer = makeTimer(
interval = { 1000L },
callback = { println("1 seconds passed.") }
)
}
在makerTimer中的callback参数前,你必须添加noinline关键字,因为Timer需要持有callback这个lambda的引用才能在合适的时机调用它,自然也就不能把它做成内联的了。
而interval这个参数只有调用makeTimer时执行一次,自然就可以内联。
但是要注意,虽然noinline也可以实现crossinline的效果,但是与crossinline有所不同
如果使用noinline,对lambda表达式的调用会转换成普通的lambda调用,即生成Function实例,然后传递实例,并调用实例的invoke()方法来运行lambda,这样自然也就禁用了非局部返回,因为此时已经在另一个函数的域内了,return根本不知道返回到哪。
总结⌗
绝大多数情况下,使用noinline引起的不能局部返回是结果,而使用crossinline禁用局部返回是目的。