關鍵字:Kotlin擴展(Extension)、inline,Kotlin反編譯Java 源碼
1. 范例
廢話少說,先上范例,來看看擴展函數有什么用。
需求:
- 將任意對象轉呼為json
- 在任意對象中添加打印日志方法,打印的日志使用類名做為TAG
實現(xiàn)代碼
新建:Any+Extension.kt
val gson
get() = Gson()
fun Any.log(msg: String) {
Log.d("_" + this::class.java.simpleName, msg)
}
fun Any.toJson(): String {
return gson.toJson(this)
}
測試代碼
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
data class Person(val name: String, val age: Int, val gender: String, val interest: String)
@Test
fun extendTestCase() {1
val person = Person("jack", 18, "男", "愛好女")
//調用log 的對象是ExampleInstrumentedTest因此TAG 日志打印的TAG是\ExampleInstrumentedTest
log(person.toJson())
}
}
2. java 實現(xiàn)
如果使用java 實現(xiàn)上面打印日志和toJson的需求的方式就是創(chuàng)建一個工具類
public class Utils {
public static final void log(Object target, String msg) {
Log.d(target.getClass().getSimpleName(), msg);
}
public static final String toJson(Object target) {
return new Gson().toJson(target);
}
}
3. Kotlin 擴展函數原理
通過范例,我們已經知道Kotlin擴展函數怎么用了,現(xiàn)在我們來探討下Kotlin擴展函數的實現(xiàn)原理。我們知道java 是靜態(tài)類型語言,無法在運行時改變其類結構(函數、屬性),只能通過繼承的方式創(chuàng)造新類來擴展屬性和方法,Kotlin 也是靜態(tài)語言,也同樣無法在運行時改變其類結構(函數、屬性)。那么,Kotlin 的擴展函數有是怎么實現(xiàn)的呢? 。我們利用Android studio 的Kotlin Tools 將Kotlin代碼,反編譯為Java源碼:
(kotlin-android 會將Kotlin 源代碼,編譯成 java class 文件,從而運行在jvm 上面)


上圖是反編譯后的Java 源碼,是不是一切都清楚了,和我們上面的Java 工具類的實現(xiàn)是一樣的。
4. 使用inline (內聯(lián)函數)修飾符優(yōu)化使用頻繁的擴展函數的調用效率
我們寫兩個實現(xiàn)同樣功能的擴展函數,一個不用inline 修飾,一個用inline 修飾。
fun Any.log(msg: String) {
Log.d("_" + this::class.java.simpleName, msg)
}
inline fun Any.log1(msg: String) {
Log.d("_" + this::class.java.simpleName, msg)
}
調用這兩個方法的代碼
@Test
fun extendTestCase() {
val person = Person("jack", 18, "男", "愛好女")
log(person.toJson())
log1(person.toJson())
}
反編譯Kotlin 字節(jié)碼查看對應的Java 實現(xiàn)

可以看到 :
在調用log1(person.toJson()) 時是直接將 log1 的函數體(內部實現(xiàn))復制了過來。
why not?下面我們來看內聯(lián)函數的說明
程序在順序執(zhí)行的過程中遇到函數調用,首先要保護現(xiàn)場(壓棧)->跳轉到函數執(zhí)行處->恢復現(xiàn)場(出棧)(此處并沒有詳細展開大概知道流程即可),這樣一進一出無疑損耗了性能,于是乎產生了內聯(lián)函數。
內聯(lián)函數并沒有壓棧出棧的操作,而是直接將內聯(lián)函數的函數體復制粘貼到了調用的位置,這樣雖然減少了性能損耗,但是編譯過程中實際的代碼量變大了,這就是簡單的空間換時間。
簡而言之:內聯(lián)函數就將其函數體復制粘貼過來
內聯(lián)函數的說明見:http://www.itdecent.cn/p/7cb426ee324c,通俗易懂
5. 小結
- 使用Kotlin 后我們可以使用擴展函數代替以前的工具類方便的實現(xiàn)一些通用的工具方法,例如打印log、toJson,showToast 等等。
- 在Android 上面的Kotlin 都是編譯Java class 文件,我們可以利用Android studio 的Kotlin Tools 將Kotlin代碼,反編譯為Java源碼來了解Kotlin 的內部實現(xiàn),特別是在使用Kotlin遇到問題時,可以通過這種方式來定位問題。
- 對于使用頻繁的擴展函數,可以使用inline 修飾符,優(yōu)化函數的調用效率。