好好说说这line三兄弟。
1. Kotlin高阶函数
要想说明白,从头捋了一下,还得从Kotlin的高阶函数说起。首先,什么是高阶函数呢?其实所谓的高,是相对而言的,就普遍性而言,函数的参数和返回值,要么没有,要么是个数值,要么是个引用类型的对象,这是低阶函数。当一个函数的参数或者返回值类型,也是一个函数类型时,这种就叫高阶函数,函数型的类型,是Kotlin中特有而Java没有的。
1 | // 这是参数类型是函数的函数 |
简单吧,高阶函数就是这样的
2. Kotlin中的::
使用两个冒号加上函数的名字,这个函数就可以被当作参数传递,这其中是为什么呢?
首先需要明确的是,函数就是函数,不是对象,然而只有对象才能被当作参数传递,所有传递函数的地方,在编译之后Kotlin都将其封装成了一个对象,通过括号调用函数类型的参数,实际上就是在调用这个函数对象的invoke方法,开发中可以直接传递函数,这本质上是个语法糖。
语法糖,一时爽。我们都知道,创建对象是有消耗的,每调用一次传递函数类型的函数,就要为这个参数创建一个对象,如果在循环中调用,那可能就是个隐藏的内存炸弹。所以,为了解决这个可能的隐患,有请inline登场。
3. inline
inline多用于修饰带有函数类型参数的函数,如果你用它来修饰一个普通函数,也可以,但是IDE会给你弹提醒,告诉你,没必要。调用被inline修饰的函数,不会增加一层调用栈,inline,顾名思义,在一条线上。所以,在编译期间,会把inline的函数直接复制到调用的位置,连同函数类型的参数也一并展开,这样就巧妙的去掉了函数参数,从而也不会增加创建函数对象的消耗了
1 | inline fun f(fp: (String) -> Unit) { |
编译后的main函数,会把f的代码复制过,大概长这个样子
1 | fun main() { |
4. noinline
看名字就知道,和inline相反,但是inline是修饰方法,noinline是修饰函数参数的,我们已经知道了,inline修饰的方法,在编译时会复制代码,并把函数参数展开。有时候,函数会有多个函数类型的参数,我们并不希望将所有参数都展开,这个时候用noinline修饰不想被展开的函数类型的参数,即可。
5. crossline
这里主要涉及return的问题,看个例子
1 | inline fun hi(callback: () -> Unit) { |
调用hi函数时,函数参数体中的return直观上看返回的是hi,然后会执行println(“main end”),但是,hi可是inline修饰的函数,还是先看看最终形态
1 | fun main() { |
这么一看,return又是返回的main函数,所以由于嵌套的函数体,return成了一个繁琐的问题,因此,Kotlin有有几个规定
- 函数参数的参数体内,不允许调用return,只有inline修饰的函数的函数参数体可以调用return,此时return返回的是外层函数
- inline修饰的函数中,不允许再嵌套调用函数参数
1
2
3
4
5
6inline fun hi(callback: () -> Unit) {
println("finish")
scope.launch {
callback() // 此处报错
}
} - 如果真的需要在inline函数中嵌套调用函数参数,那么就用crossline修饰这个参数,但是,被修饰参数的函数体中,便不能再调用return
- 上面只是说不可以单独调用return,但是都可以调用return@label来手动指定返回锚点
总结下来就是,inline函数中没有被crossline修饰的函数参数体中,可以调用return,返回的是外层函数。同时,只有被crossline修饰的函数参数,才可以嵌套调用。
6. 总结
- inline:修饰函数,被修饰的函数编译后将代码复制到调用处,并将函数类型的参数展开
- noinline:修饰函数类型的参数,被修饰后将不再展开
- crossline:修饰函数类型的参数,被修饰后可以在inline函数中嵌套调用