Item 48 : 給高階函數使用 inline 修飾子
所謂高階函式是接受其他函式作為參數或返回函式的函式,可以理解成函式裡面還有函式,所以稱函式俄羅斯娃娃。Kotlin 中的 inline 函式是為了效能考量而引入的特性。當我們使用高階函式時,會有額外的執行期時開銷,因為在 JVM 本身沒有 function type 的概念。inline function 這題,朱凱老師講的很好,看完就滿夠的
所以在執行時,傳遞的函式參數需要生成額外的物件來包裝 function type 。這些開銷在頻繁呼叫的情境下,尤其是在迴圈內,可能導致效能問題。
所以幾乎 Kotlin 內建的高階函式(如 Collection 的 filter, map) 都是 inline 函式
inline fun repeat(times: Int,action: (Int) -> Unit) {
for (index in 0 until times) {
action(index)
}
}
inline 關鍵字告訴編譯器,當該函式被呼叫時,不要真的呼叫它,而是將函式的程式碼嵌入到呼叫處。這樣,我們就可以避免因函式呼叫而產生的開銷。
repeat(10) {
print(it)
}
//after compilation
for (index in 0 until 10) {
print(index)
}
當你在使用 inline 函式時,有時可能會遇到需要更細緻控制 lambda 行為的情境。在這些情境中,noinline 和 crossinline 這兩個修飾詞會派上用場。以下我們來介紹它們:
當一個 inline 函式接受多個 lambda 參數時,你可能不想要所有的 lambda 都被嵌入。這時,你可以使用 noinline 修飾詞來指示編譯器哪些 lambda 不應該被嵌入。而常見的是把傳入的 lambda 再當作回傳值,因為這時如果他是 inline 就無法當作回傳值(因為該 function type 被展開)。這時 IDE 也會提示你 noinline
在 inline 函式中,有時你會遇到需要在另外一個執行環境(例如異步回調)中呼叫 lambda 的情境。但由於 lambda 可能包含 return 關鍵字,這可能會導致整個外部函式(而不只是 lambda 本身)提早結束。為了避免這種情況,Kotlin 提供了 crossinline 修飾詞,使得在該 lambda 內不能有非局部的返回。
inline fun runAsync(crossinline body: () -> Unit) {
Thread {
body()
}.start()
}
fun main() {
runAsync {
println("This runs in another thread.")
// return // This would be an error because of 'crossinline'
}
}
在上述例子中,如果沒有 crossinline,lambda 內的 return 會導致 main 函式提早結束。但由於我們使用了 crossinline,所以這個 return 是不被允許的。
總之,noinline 和 crossinline 都是為了提供更多的控制以滿足特定的需求而設計的修飾詞。
IntelliJ 或 Android 其實會幫你注意這些狀況。所以這兩個只要相信 IDE 的題示就可以了。
與 PSY大叔合作的舞台,也是很炸!