透過 inline 函式可以優化效能,所有的 Collection API 絕大部分都是 inline 函式,避免頻繁呼叫這些函式的時候,造成不必要的浪費。
說明
講到 inline 首先我們會先討論一下 Lambda function,什麼是 Lambda function?
我們通常在 Java 會看到這樣的寫法。
/* Java */
public interface OnClickListener{
void onClick(View v);
}
而在某個類別會透過上面的介面來完成某些事件處理。
/* Java */
public class Button {
public void setOnClickListener(OnClickListener l) { ... }
}
所以當我們實作的時候就會長這樣。
/* Java */
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
...
}
}
那我們在 Kotlin 就可以寫成這樣。
button.setOnClickListener { view -> ... }
這種就稱之為 Lambda,但是要注意的一點是這個介面只能有一個實作方式,我們稱之為 Single Abstract Method(SAM),SAM 建構方法是編譯器生成的一種函式,讓你能夠快速的實作介面的一種顯性轉換,透過這種快速轉換去掉冗長的語法贅詞,可以讓我們專注在處理的事情上。
除了快速轉換語法以外,SAM 還可以幫忙把結果傳遞出去,舉個例子來說。
val runnable = Runnable{ println("hello") }
這樣一來我們就可以把 runnable 這個變數傳遞到所需的地方。
回到我們前面所討論的,什麼是 inline 函式?它用來解決什麼問題呢?
假設你有一個函式像下面這樣。
fun sum(a: Int, b: Int, lambda: (result: Int) -> Unit): Int {
val r = a + b
lambda.invoke(r)
return r
}
然後你就去 call 它。
fun main(args: Array<String>) {
sum(1, 2) { println("Result is: $it") }
}
所以當你透過 Android Studio 的 decompile 工具去看的時候,你會發現 Java 程式長這樣。
//decompile
public static final void main(@NotNull String[] args) {
//...
sum(1, 2, (Function1)null.INSTANCE);
}
也就是說,每一個 Lambda 在處理的時候,都會生成一個 instance,所以當我們如果跑迴圈來處理這個函式的時候,就會不斷地產生新的 instance,那這樣該怎麼優化呢?
Kotlin 提供了 inline 函式就是為了解決這個問題,透過 inline 關鍵字,我們同樣的程式碼反組譯完就會變成下面這樣。
//decompile
public static final void main(@NotNull String[] args) {
//...
byte a$iv = 1;
int b$iv = 2;
int r$iv = a$iv + b$iv;
String var9 = "Result is: " + r$iv;
System.out.println(var9);
}
也就是說少掉了 function call 的部分,直接把 function 的內容複製貼上到你 call 的那個地方,所以,就會節省掉 function call 的額外支出,這樣一來,如果你的 function 是很頻繁的呼叫的話,就可以有效地節省這一塊效能,而且我們可以很容易發現, Collection API 幾乎全部都宣告成為 inline 函式,就是為了提高效能。