非局部返回
我们只能对具名或匿名函数使用正常的、非限定的 return 来退出。 这意味着要退出一个 lambda 表达式,我们必须使用一个标签,并且在 lambda 表达式内部禁止使用裸 return,因为 lambda 表达式不能使包含它的函数返回:
fun ordinaryFunction(block: () -> Unit) {
println("hi!")
}
fun foo() {
ordinaryFunction {
return // 错误:不能使 `foo` 在此处返回
}
}
// 如果想要返回要使用标签
fun foo() {
ordinaryFunction @myMark {
return@myMark
}
}
fun main() {
foo()
}
但是如果 lambda 表达式传给的函数是内联的,该 return 也可以内联,所以它是允许的:
inline fun inlined(block: () -> Unit) {
println("hi!")
}
fun foo() {
inlined {
return // OK:该 lambda 表达式是内联的
}
}
一些内联函数可能调用传给它们的不是直接来自函数体、而是来自另一个执行上下文的 lambda 表达式参数,例如来自局部对象或嵌套函数。在这种情况下,该 lambda 表达式中也不允许非局部控制流。为了标识这种情况,该 lambda 表达式参数需要用 crossinline 修饰符标记:
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ……
}
inline
通过内联(即函数内容直插到调用处)的方式来编译函数,优化代码结构,从而减少函数类型的对象的创建(函数类型的参数在传参进去的时候会生成一个局部的变量,如果在for循环中使用就可能会重复创建对象)
fun hello(postAction:()-> Unit) {
println("hello")
postAction()
}
// 调用处
fun main() {
hello {
println("Bye")
}
}
// 实际编译代码(大致)
fun main() {
// 在这里创建了这个局部变量 如果在for循环中就会重复创建
val post = object : Function0<Unit> {
override fun invoke() {
return prinln("Bye")
}
}
hello(post)
}
noinline
局部关掉这个优化,来摆脱不能把函数类型的参数当对象使用的限制,比如把函数类型的参数返回出去
// 会报错的写法
fun hello(postAction:()-> Unit) {
println("hello")
postAction()
return postAction // 这里会编译报错
}
// 修改后
fun hello(noinline postAction:()-> Unit) {
println("hello")
postAction()
return postAction
}
crossinline
让内联函数里面的函数类型的参数可以被间接调用,代价不能在Lambda表达式中使用return
// 会报错的写法
inline fun hello(postAction:()-> Unit) {
println("hello")
runOnUiThread {
postAction() // 这里会报错
}
}
// 调用
fun main() {
hello {
println("Bye")
return
}
}
// 修改后
inline fun hello(crossinline postAction:()-> Unit) {
println("hello")
runOnUiThread {
postAction()
}
}
// 调用
fun main() {
hello {
println("Bye")
// return // 使用crossinline 后不能再使用return
}
}
kotlin 1.3 版本break 和 continue 在内联的 lambda 表达式中还不可用 ,后面可能会支持