官方文檔: http://kotlinlang.org/docs/reference/inline-functions.html
1.内聯函數的概念和作用
使用高階函數(higher-order functions)會導緻一些性能的損耗:
每個函數都是對象,且會捕獲閉包closure(即變量會在函數體内被通路),
函數對象/類會增加記憶體配置設定,而且虛拟調用棧也會增加額外記憶體開銷!
可用内聯函數(inline function)消除這些額外記憶體開銷,
說白了就是在調用處插入函數體代碼,以此減少建立函數棧和對象的記憶體開銷!
被inline修飾的函數或lambda表達式,在調用時都會被内聯(在調用處插入函數體代碼)
inline fun lock<T>(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
print("開始************")
lock(l) { foo() }
print("結束************")
//編譯器實際生成以下代碼(就是直接把代碼插入到調用處):
print("開始************")
l.lock()
try {
foo()
}
finally {
l.unlock()
}
print("結束************")
很明顯,内聯可能導緻編譯器生成的代碼增加,但如果使用得當(不内聯大函數),在性能上有很大提升,
尤其是在循環的megamorphic處調用!
禁用内聯(noinline)
如果内聯函數的有些(作為參數)lambda表達式不是内聯,可用noinline修飾符函數參數!
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ……
}
2.非局部傳回(Non-local returns)
在内聯的lambda表達式中退出包含它的函數稱為非局部傳回(Non-local returns)!
在Kotlin中,正常return(沒有限定符@)表示退出一個命名或匿名函數
是以要退出lambda表達式,return需要加限定符@标簽,
在非内聯的lambda表達式中禁用正常return(沒有限定符@):
fun f1(p: ()->Unit) {
p()
}
inline fun f2(p: ()->Unit){
p()
}
fun main(args: Array<String>) {
f1{
//f2和lambda表達式都不是内聯
println("Hello, world!")
return //編譯錯誤,此處不允許return
}
f2{
//f2是内聯函數,lambda表達式也是内聯
println("Hello, world!")
return //編譯正确,可以return
}
listOf(1,2,3).forEach {
//forEach是内聯
if(it==2) return //編譯正确,可以return
print(it)
}
}
此外,一些内聯函數參數不是直接來自函數體,而是來自另一個上下文的lambda表達式參數,
例如來自局部對象或嵌套函數,lambda表達式也不允許非局部傳回!
為了辨別這種情況,該lambda表達式參數需要用crossinline修飾符:
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
}
目前,break和continue在内聯的lambda表達式中還不可用,未來kotlin計劃支援!
3.類型參數的具體化(Reified type parameters)
執行個體(類型參數-泛型):
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)
1.為簡化函數調用,内聯函數支援類型參數具體化:
inline fun <reified T> TreeNode.findParentOfType(): T? {
//用reified修飾符類型參數T,可在函數内通路T,像通路普通類,
//由于函數是内聯的,無需反射,正常操作符如!is和as都能用,
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
//該函數調用簡潔優雅
treeNode.findParentOfType<MyTreeNode>()
2.雖然多數情況不需要反射,但仍然可對具體化的類型參數使用:
inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>) {
println(membersOf<StringBuilder>().joinToString("\n"))
}
3.泛型的具體化條件
普通函數(未标記為内聯函數)不能具體化參數!
在運作時無法表示的類型(類似Nothing虛構類型)不能作為具體化參數的實參!
4.内聯屬性(Inline properties)
自kotlin 1.1起, inline可修飾[沒有幕後字段]屬性通路器get/set函數(方法)
在調用處,内聯通路器get/set函數,和内聯函數一樣内聯,沒什麼差別!
1.修飾單個屬性通路器:
val foo: Foo
inline get() = Foo()
var bar: Bar
get() = ……
inline set(v) { …… }
2.修飾整個屬性,将兩個通路器都标記為内聯:
inline var bar: Bar
get() = ……
set(v) { …… }
簡書:http://www.jianshu.com/p/79396a5056d7
CSDN部落格: http://blog.csdn.net/qq_32115439/article/details/73929039
GitHub部落格:http://lioil.win/2017/06/29/Kotlin-inline-fun.html
Coding部落格:http://c.lioil.win/2017/06/29/Kotlin-inline-fun.html