使用高阶函数会给运行时带来一些坏处:每一个函数都是一个对象,捕获闭包(如:访问函数体内的变量),内存分配(函数对象或Class),虚拟调用引入的运行过载。 使用内联Lambda表达式在多数状况下能够消除这种过载。好比下面的函数就是这种状况下的很好的例子,lock()函数能够很容易地在调用点进行内联扩展。java
lock(l){ foo() }
编译可以产生下面的代码,而不是建立一个函数对象参数,生成调用。闭包
l.lock() try { foo() } finally { l.unlock() }
也是咱们一开始想要的。 为了让编译器可以这样执行,须要用inline修饰符来标记lock函数。ide
inline fun lock<T>(lock: Lock , body: () -> T): T{ ... }
inline修饰符既影响函数对象自己,也影响传入的Lambda参数:二者都会被内联到调用点。函数
编译预处理器对内联函数进行扩展,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return等过程,从而提升了运行速度。 使用内联函数的优势,在函数被内联后编译器就能够经过上下文相关的优化技术对结果代码执行更深刻的优化。 内联不是万能药,它以代码膨胀为代价,仅仅省去了函数调用的开销,从而提升程序的执行效率。优化
说明:函数调用开销并不包括执行函数体所须要的开销,而是仅指参数压栈、跳转、退栈和返回等操做。若是执行函数体内代码的时间比函数调用的开销大得多,那么内联函数的效率收益会笑不少。另外一方面每一处内联函数的调用都要拷贝代码,将使程序的总代码增大、消耗更多的内存空间。ui
若是只须要在内联函数中内联部分Lambda表达式,能够使用noinline来标记不须要内联的参数。spa
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { // ... }
内联Lambda只能在内联函数中调用或做为内联参数,但noinline的Lambda可随意使用。
说明:没有内联函数参数和reified type parameters的内联函数,编译器会发出警告,由于内联这样的函数不见得有好处。code
在Kotlin中能够使用正常、无条件的return退出有名和匿名函数,也意味须要使用一个标签来退出Lambda,在Lambda中禁止使用赤裸return语句,由于Lambda不可以使闭合函数返回。对象
fun foo(){ ordinaryFunction{ return // ERROR: can not make `foo` return here } }
若是Lambda传入内联函数,则返回也是被内联,因此被容许。内存
fun foo(){ inlineFunction { return // OK: the lambda is inlined } }
这样的return(位于在Lambda中,但可以退出闭合函数)被称为非局部返回。Kotlin使用这种构造在有循环条件的闭合内联函数中。
fun hasZeros(ints: List<Int>): Boolean{ ints.forEach{ if(it == 0) return true // returns from hasZeros } return false }
一些内联函数可能不是从函数体中直接调用传入的Lambda参数,而是从其余的执行上下文,如本地对象或嵌套函数。在这些状况下,non-local 控制流则不容许出如今Lambda中。使用crossinline修饰符来标记。
inline fun f(crossinline body: () -> Unit) { val f = object: Runnable { override fun run() = body() } // ... }
有时须要访问传入函数中参数的类型。例如:
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修饰符限制参数类型,能够在内联函数中访问,就像是普通的Class。由于函数是内联的,不在须要反射,像!is和as的普通操做符执行。也能够像上述说的那样调用。
myTree.findParentOfType<MyTreeNodeType>()
尽管反射在不少状况不须要,仍须要使用它来具体话参数类型。
inline fun <reified T> membersOf() = T::class.members fun main(s: Array<String>) { println(membersOf<StringBuilder>().joinToString("\n")) }
inline修饰符能够用在没有Backing Filed属性的访问函数。能够注解单独属性的访问函数。
val foo: Foo inline get() = Foo() var bar: Bar get() = ... inline set(v) { ... }
甚至能够注解整个属性,让属性访问函数都变为内联函数。
inline var bar: Bar get() = ... set(v) { ... }
在调用时,内联访问函数与常规内联函数调用方式同样。
阅读原文