Kotlin-内联函数
来源:互联网 发布:算法导论第三版pdf下载 编辑:程序博客网 时间:2024/06/10 09:47
使用高阶函数肯定有不小的代价:每个函数都是一个对象并且它还是闭包,例如,函数内的局部变量只能在函数体内访问,内存分配(函数对象和类)和虚拟调用都会导致运行时开销增大。
但它在很多情况下的出现的开销还是可以通过内联lambda表达式消除,在下面的这个函数就是一个很好的实例,例如lock()函数可以很轻易的内联到调用处,考虑以下情况:
lock(l) { foo() }
编译器可能会生成以下代码,而不是为参数创建函数对象并生成调用
l.lock()try { foo()}finally { l.unlock()}
按这样来看,这不是我们从一开始就想要的吗?
要想编译这样做,我们需要用inline来修饰lock()函数
inline fun lock<T>(lock: Lock, body: () -> T): T { // ...}
inline修饰符会影响函数本身和lambda表达式传给它的值,所有这些将内联到调用处,
内联会导致代码量的增加,但如果我们使用得当(不要在大函数中使用),那么它将只会消耗部分性能,特别是在循环内部使用(especially at “megamorphic” call-sites inside loops.)
非内联(noinline)
如果你只想lambdas的部分传递是内联的,那么你可以在函数参数中使用noinline修饰符来避免内联
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { // ...}
内联lambda只能叫里面的内联函数或内联参数传递,但被noinline修饰的话则可以任意使用,存储在字段中、经过字段传递等.
==需要注意的是,如果内联函数没有可内联的函数参数,并且没有具体的参数类型,那么编译器就会发出警告,因为内联等功能很可能是有益的(你可以抑制警告如果你确信内联是必要的).==
非本地返回
在Kotlin中,我们只可以使用普通且不是任意的return来退出命名函数或匿名函数,这就意味着我们要退出lambda必须使用标签,在lambda内部是禁止直接使用return,因为在lambda中使用return保证不了函数闭合:
fun foo() { ordinaryFunction { return // ==错误用法,这里不能使foo返回== }}
但如果函数是通过lambda来达到内联,那么return就和内联一样有效,比如它可以这样:
fun foo() { inlineFunction { return // OK,lambda内联 }}
像这样的return(在lambdas中就可以退出闭合函数)被称为非本地返回,我们已经习惯了这种包含内联函数的循环结构:
fun hasZeros(ints: List<Int>): Boolean { ints.forEach { if (it == 0) return true // returns from hasZeros } return false}
==注意:在一些局部变量或嵌套函数中,一些内联函数作为Lambda表达式参数的时候,不可以直接在函数体内使用,但可以在另外一个上下文中使用。在这种情况下,在Lambdas中也是不允许非本地控制流。为解决这种情况,Lambdas的参数需要使用crossinline关键字来修饰:==
inline fun f(crossinline body: () -> Unit) { val f = object: Runnable { override fun run() = body() } // ...}
在内联Lambdas中,break和continue关键字同样也是不适用,但在未来版本中会加入。
参数化类型
有时候,我们需要通过参数来访问一些类型:
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? { var p = parent while (p != null && !clazz.isInstance(p)) { p = p.parent } @Suppress("UNCHECKED_CAST") return p as T?}
在这里,我们遍历继承树并用反射去检查这个节点是否符合类型,这种做法很好,但这种调用方式不是最好的。
treeNode.findParentOfType(MyTreeNode::class.java)
我们实际上需要的是将一个类型传递给这个函数,它可以这样使用:
treeNode.findParentOfType<MyTreeNode>()
为了达到这个效果,内联函数支持参数化类型,因此,我们还可以这样写:
inline fun <reified T> TreeNode.findParentOfType(): T? { var p = parent while (p != null && p !is T) { p = p.parent } return p as T?}
我们明确的指定参数类型用reified来修饰,现在我们可以把它当做普通类那样来直接访问,因为这个函数是内联函数,不需要反射,像那些!is和as的操作都可以正常使用,像上面提到的我们可以这样使用:
myTree.findParentOfType<MyTreeNodeType>()
在很多情况下,不一定需要使用反射,我们还可以使用它与具体的类型参数:
inline fun <reified T> membersOf() = T::class.membersfun main(s: Array<String>) { println(membersOf<StringBuilder>().joinToString("\n"))}
普通函数(就是没有被inline标识的函数)是不可以有具体类型参数的,没有在运行时被明确表示的类型(非具体类型参数或虚构类型Nothing)是不可以被用具体类型参数.
内联属性(版本1.1以后)
当属性的访问器没有隐性支持属性时可以被inline修饰,可以标识自定义属性的访问器:
val foo: Foo inline get() = Foo()var bar: Bar get() = ... inline set(v) { ... }
还可以对整个属性进行标识,这表示该属性的所有访问器都被inline修饰:
inline var bar: Bar get() = ... set(v) { ... }
被调用的时候,内联访问器具备和内联函数一样的规则。
- Kotlin-内联函数
- Kotlin基础教程-内联函数
- Kotlin-内联函数
- Kotlin 第十六章:内联函数
- Kotlin的inline内联函数
- Kotlin-23.内联函数(Inline Functions)
- Kotlin学习之-6.3 内联函数
- kotlin学习笔记——内联函数
- Kotlin函数与Lambdas(三)--- 内联函数
- Kotlin语法(十九)-内联函数(Inline Functions)
- Kotlin Reference (十六) 函数和lambda表达式:内联函数、内联属性
- Kotlin内联:noinline与crossinline
- 内联函数
- 内联函数
- 内联函数
- 内联函数
- 内联函数
- 内联函数
- 进程与线程的区别与联系
- android里CountDownTimer类的用法
- “惨痛”的一次面试之旅
- Linux程序崩溃core使用(续)
- Linux Man 手册
- Kotlin-内联函数
- 博客阅读记录
- BZOJ 1941 [Sdoi2010] Hide and Seek
- VLD内存泄露库的使用
- IDEA中安装MyBatis Plugins(Mybatis-Generator)插件及破解方法
- Eclipse解决Tomcat版本过低不能发布问题
- nodejs学习(9)nunjucks模板引擎
- 《摆渡人》
- 555定时器