奇技淫巧之Kotlin 擴展函數(一)

關鍵字:Kotlin擴展(Extension)、inline,Kotlin反編譯Java 源碼

1. 范例

廢話少說,先上范例,來看看擴展函數有什么用。

需求:

  1. 將任意對象轉呼為json
  2. 在任意對象中添加打印日志方法,打印的日志使用類名做為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 上面)

image
image

上圖是反編譯后的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)

image

可以看到 :

在調用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. 小結

  1. 使用Kotlin 后我們可以使用擴展函數代替以前的工具類方便的實現(xiàn)一些通用的工具方法,例如打印log、toJson,showToast 等等。
  2. 在Android 上面的Kotlin 都是編譯Java class 文件,我們可以利用Android studio 的Kotlin Tools 將Kotlin代碼,反編譯為Java源碼來了解Kotlin 的內部實現(xiàn),特別是在使用Kotlin遇到問題時,可以通過這種方式來定位問題。
  3. 對于使用頻繁的擴展函數,可以使用inline 修飾符,優(yōu)化函數的調用效率。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容