kotlin小记b-内联函数

575 阅读2分钟

非局部返回

我们只能对具名或匿名函数使用正常的、非限定的 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 表达式中还不可用 ,后面可能会支持