1. 程式人生 > >Kotlin的inline行內函數

Kotlin的inline行內函數

方法呼叫流程

呼叫一個方法是一個壓棧和出棧的過程,呼叫方法時將棧針壓入方法棧,然後執行方法體,方法結束時將棧針出棧,這個壓棧和出棧的過程會耗費資源,這個過程中傳遞形參也會耗費資源。

為什麼需要inline

有些簡單的方法會被頻繁呼叫,什麼叫簡單的方法呢,舉個例子:

fun <T> check(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()
        } finally {
            lock.unlock()
} }

這個check方法的方法體中,不會將它的形參再傳遞給其他方法。我們呼叫一下check方法:

  check(l, {"我是lambda方法體"})//l是一個Lock物件

對於編譯器來說,呼叫check方法就要將引數l和lambda表示式{"我是lambda方法體"}進行傳遞,還要將check方法進行壓棧出棧處理,這個過程就會耗費資源。

如果我們把check方法刪除,直接執行check方法的方法體:

.
        l.lock()
        try {
            return "我是lambda方法體"
        } finally
{ l.unlock() }

這樣做的效果和呼叫check方法是一樣的,而且不需要壓棧出棧了,但是程式碼是寫給人看的,這樣寫明顯產生了程式碼壞味道,老司機會告訴你,這幾行程式碼需要抽成一個方法,避免多處呼叫產生冗餘程式碼。於是你就老老實實把這幾行程式碼抽成了check方法,那麼如上所述,一旦這個方法被頻繁呼叫,壓棧出棧將會帶來效能問題。針對這個問題,kotlin引入了inline關鍵字。我們在check方法前加上inline關鍵字:

inline fun <T> check(lock: Lock, body: () -> T)
: T { lock.lock() try { return body() } finally { lock.unlock() } }

然後我們再呼叫check方法,編譯器就會在編譯期幫我們進行優化:
將我們寫的程式碼

  check(l, {"我是lambda方法體"})//l是一個Lock物件

換成

.
        l.lock()
        try {
            return "我是lambda方法體"
        } finally {
            l.unlock()
        }

也就是說inline關鍵字實際上增加了程式碼量,但是提升了效能,而且增加的程式碼量是在編譯期執行的,對程式可讀性不會造成影響。

noinline

如果check方法中的引數需要傳遞給其他非inline方法:

inline fun <T> check(lock: Lock, body: () -> T): T {
            lock.lock()
            try {
                otherCheck(body)//會報錯
                return body()
            } finally {
                lock.unlock()
            }
    }

    fun <T> otherCheck(body: ()-> T){

    }

那麼呼叫otherCheck是會報錯的,因為check方法中的形參body現在已經inline了,不是一個函式物件了,也就不能作為一個引數傳遞了,除非在body引數前加上noinline關鍵字。