inline 主要用于展开铺平函数,用于高频访问但是代码不是很多的方法,减少函数对象的定义
fun <T> List<T>.normalForeach(action:(T)->Unit){for(item in this){action(item)}
}inline fun <T> List<T>.inlinedForeach(action: (T) -> Unit){for(item in this){action(item)}
}
inline就好像把函数内部的代码拷贝一份到调用的地方,减少了函数对象的中间产生。
带来的变化是,可以非本地的返回
fun main(){val list = (1..100).toList()list.normalForeach { println(it)return@normalForeach}list.inlinedForeach { println(it)return}}
普通的函数只能返回 return@normalForeach
内敛函数,因为相当于被拷贝过来了,可以直接返回到main,
另外内敛带来的变化还有
fun main(){val list = (1..100).toList()CoroutineScope(Dispatchers.Default).launch {delay(1000)list.normalForeach {println(it)delay(1000) //错误!非内敛,无scope环境return@normalForeach}list.inlinedForeach {println(it)delay(1000)return@launch}}
}
我们启动了一个协程,但是在normalForeach的lambda中无法使用delay,因为这里其实是一个函数对象,会指向到另外一个函数方法,没有协程的上下文环境,但是内敛函数就不一样了,他相当于是把lambda中的内容拷贝平铺过来了,所以,在inlinedForeach中可以使用协程的上下文环境,delay可以执行。
但是当一个函数被声明为内敛函数的时候,只能直接的运行函数参数,或者在另外一个内敛函数中运行函数参数,
inline fun <T> List<T>.inlinedForeach(action: (T) -> Unit){for(item in this){action(item) //直接运行方法参数kotlin.run {action(item) //或者在另外一个内敛函数中运行}Thread{action(item) //报错!不可以跨scope或者context}}
}
那如果我们必须要参数中的函数可以切换调用呢?
那我们需要在函数参数上crossinline关键字,允许函数被跨上下文调用
inline fun <T> List<T>.inlinedForeach(crossinline action: (T) -> Unit){for(item in this){action(item) //直接运行方法参数kotlin.run {action(item) //或者在另外一个内敛函数中运行}Thread{action(item) //因为crossinline关键字,不报错了}}
}
这样,就可以在Thread中调用action了,但是因为函数参数被声明了crossinlne,原本的非本地返回就不可以用了
所以要不要非本地返回和跨上下文调用,只能二选一
另外,当把一个函数声明为inline的时候,他的lambda参数也是inline的,所以无法通过函数地址引用的方式来调用
但是我们需要再inline声明的函数参数中,通过地址的方式来引用的话,就要noinline来声明
inline fun <T> List<T>.inlinedForeach(crossinline action: (T) -> Unit,noinline onError: (Exception) -> Unit
) {try {for (item in this) {action(item) //直接运行方法参数kotlin.run {action(item) //或者在另外一个内敛函数中运行}Thread {action(item) //因为crossinline关键字,不报错了}}} catch (e: Exception) {e.printStackTrace()handleError(e,onError)}}fun handleError(exception: java.lang.Exception,onError: (Exception) -> Unit) {println("handle ")onError(exception)}