Kotlin邊用邊學:Inline Functions的適用場景
Key Takeaways(劃重點):
- Collection自己提供的處理函式(forEach/map...)都支援inline,能用就別自己寫迴圈
- 在嚴格執行了上條後,你基本上就不怎麼需要了解/寫inline functions了
- 升級第三方庫後,務必重新編譯你的專案
- 自己寫for/while迴圈,且迴圈的N值很大時,是使用inline function的一個切入點
- 大函式不使用inline
- 理解不了的時候看反編譯生成的Java有奇效
- 看原始碼可以增進了解
基本介紹
Inline functions,中文大概就是內聯/內嵌函式,字面的意思就是把內部(偷偷的)把被呼叫函式的程式碼連線(Copy)過來,具體看下程式碼和反編譯的結果:
原始碼:
fun main(args: Array<String>) {
val localGreeting = "Hello from main"
Demo().withPublicField { println(localGreeting) }
}
class Demo() {
val title = "title in demo"
fun withPublicField(otherFun: () -> Unit) {
println("Call from withPublicField, title: $title")
otherFun()
}
}
檢視反編譯的Java:(IntelliJ IDEA/Android Studio Tools -> Kotlin -> Show Kotlin Bytecode
,別忘了點選Decompile
按鈕)
public final class MainKt {
public static final void main(@NotNull String[] args) {
final String localGreeting = "Hello";
(new Demo()).withPublicField((Function0)(new Function0() {
...
public final void invoke() {
String var1 = localGreeting;
System.out.println(var1);
}
}));
}
}
public final class Demo {
private final String title = "title_private";
@NotNull
public final String getTitle() { return this.title; }
public final void withPublicField(@NotNull Function0 otherFun) {
String var2 = "Call from withPublicField, title: " + this_$iv.getTitle();
System.out.println(var2);
otherFun.invoke();
}
}
由於Java的function不是first-class member,所以其高階函式(higher-order function)的使用,實際上存在著封裝function成對應的wrapper class的例項,並對可見範圍內(closure)的變數存在引用/訪問。這實際上是以一定的資源損耗換來的便利性。對於我們上述的程式碼:
第4行
(Function0)(new Function0() {...}
即是function轉化成wrapper class例項第7行
String var1 = localGreeting;
即是對可見範圍變數的引用
在平常使用中,這個損耗是微小可忽略的,但如果是被一個N次(N值很大)迴圈中呼叫,那積少成多的損耗就是一個值得重視的問題了。
對於這個問題,Kotlin的處理方式是直接在編譯期將原來的higher-order function的執行/實現程式碼,copy到呼叫處。具體看程式碼(唯一的改動點:原函式前新增了inline關鍵字):
inline fun withPublicField(otherFun: () -> Unit) {
...
}
然後檢視生成的Java二進位制:
public final class MainKt {
public static final void main(@NotNull String[] args) {
String localGreeting = "Hello";
Demo this_$iv = new Demo();
String var3 = "Call from withPublicField, title: " + this_$iv.getTitle();
System.out.println(var3);
System.out.println(localGreeting);
}
}
注意對比兩次生成的java程式碼:原先的L4~L10,現在的L4~L7。可以看到,新的Java程式碼很簡單粗暴的把withPublicField的實現程式碼copy進入了main函式(自然移除了對withPublicField的呼叫)。
好處
- 不需要額外建立wrapper class instance
- 不需要維護可見範圍內的變數引用/呼叫
代價肯定有的,不然就所有的方法都設成inline不就完了:-)
使用注意點
任何inline function的修改,必須重新編譯後才生效
這個從之前反編譯的程式碼可以推斷出,畢竟呼叫inline function的程式碼已經被抹除,替換成了inline function的實現程式碼。重新編譯才能再次重複這個替換過程。這個對於呼叫第三方庫的inline函式尤其需要注意。
private變數
之前我們看到植入的程式碼中有這麼一行程式碼(L5):
String var3 = "Call from withPublicField, publicTitle: " + this_$iv.getPublicTitle();
這裡,getPublicTitle()
如果是個private會怎樣,相信大家可以猜測到。具體可以用程式碼驗證:
class Demo() {
private val title = "title in demo"
...
}
修改成private可見性後,程式碼編譯出錯(this_$iv.getPublicTitle()在main函式是不可見了)
當然inline函式的寫法,除了這個變數的可見性還有其他,譬如:Non-local returns,但只要理解了其編譯時的copy行為後,很多就水落石出了。(好的IDE其實還是給了很多有用的提示的,所以儘管寫,讓IDE去操這個心吧)
原始碼檢視/驗證
Iterator.forEach
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
for (element in this) action(element)
}
可以看到,自帶的Collection的函式都早已支援了inline了。所以,能使用自帶的Collection函式就別寫自己的迴圈了。
希望這篇博文能對你有所幫助,喜歡的話點個贊吧?!
更多Kotlin的實用技巧,請參考《Kotlin邊用邊學系列》
Bonus
- 一個輕巧的反編譯GUI:bytecode-viewer
- 關於inline functions帶來的效能提升,可以看這篇文章:Effective Kotlin: Consider inline modifier for higher-order functions
相關文章
- 打破邊界,邊緣計算有何應用場景?
- 史上最全的邊緣計算應用場景
- 邊緣應用場景需求興起,FusionCube解碼智慧邊緣!
- 邊緣雲端計算典型應用場景
- 這款老牌 “裝機必備” 應用,一邊用 Kotlin 換血,一邊深耕精細市場Kotlin
- Spark適用於哪些場景?不適用於哪些場景?Spark
- [邊學邊練]用簡單例項學習React單例React
- CDN適用哪些場景?
- SPM適用的場景和示例
- INDEX SKIP SCAN適用場景Index
- 基於雲邊協同架構的五大應用場景革新架構
- MongoDB的優勢和適用場景MongoDB
- 邊聊邊複製,邊修改邊測試,利用chatgpt用laravel框架做一個部落格應用ChatGPTLaravel框架
- Streaming特性和適用場景
- 設計模式適用場景整理設計模式
- 企業雲盤適用哪些應用場景
- 邊學邊寫——母函式及其在中學數學競賽中的運用(一)函式
- 區塊鏈不適用的若干場景區塊鏈
- 廣告場景下雙邊市場的實驗設計
- 邊緣叢集場景下的映象快取快取
- Kotlin基礎:抽象屬性的應用場景Kotlin抽象
- OpenYurt v0.3.0 重磅釋出:全面提升邊緣場景下應用部署效率
- iOS 邊學邊記iOS
- 在邊緣計算場景中使用Dapr
- 遷移學習的基礎研究問題及適用場景遷移學習
- 邊緣計算場景下雲邊端一體化的挑戰與實踐
- 全息投影技術所適用的場景分析
- 談談 Redux 與 Mobx 思想的適用場景Redux
- 本地索引和全域性索引的適用場景索引
- css之左邊定寬右邊自適應CSS
- 走進智慧邊緣小站IES,從其應用場景瞭解產品優勢
- 低程式碼適用於哪些應用開發場景
- SpringBoot邊學邊用(二)SpringBoot 整合 Mybatis(xml檔案形式)Spring BootMyBatisXML
- 【從0到1學習邊緣容器系列2】之 邊緣應用管理
- 聚焦邊緣計算場景,打造雲邊端一體化容器雲平臺
- BITMAP CONVERSION FROM ROWIDS 適用場景
- 索引設計(組合索引適用場景)索引
- css爸爸!一邊固定,另一邊自適應的方法CSS