kotlin inline、noline、crossinline、reified

使用高階函數時,每個函數都是一個對象,函數調用時還有入棧出棧的開銷。

以lock函數爲例

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

調用處

  val l = ReentrantLock()
  lock(l) {

  }

反編譯成JAVA

  ReentrantLock l = new ReentrantLock();
  KtxKt.lock((Lock)l, (Function0)null.INSTANCE);

lambda編譯成了函數對象Function0,這樣每次調用都會生成函數對象,頻繁創建很不友好。

inline

加入inline關鍵字

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

inline 修飾符影響函數本⾝和傳給它的 lambda 表達式:所有這些都將內聯到調⽤處。

上述是官方解釋,翻譯成人話:inline修飾的函數,其本身和參數lambda表達式的內容會直接替換到調用處。省去了函數調用的開銷,和生成函數對象。

調用處

  val l = ReentrantLock()
  lock(l) {

  }

反編譯成JAVA

  ReentrantLock l = new ReentrantLock();
  int $i$f$lock = false;
  ((Lock)l).lock();

  try {
      boolean var3 = false;
      Unit var6 = Unit.INSTANCE;
  } finally {
      ((Lock)l).unlock();
  }

加入inline關鍵字會產生一個新的問題:內聯的 lambda 表達式只能在內聯函數內部調⽤或者作爲可內聯的參數傳遞。

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

fun <T> get(body: () -> T): T {
    return body()
}

編譯器會報錯,想要將inline的lambda傳遞到非inline函數,需加上noinline關鍵字禁用內聯。

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

fun <T> get(body: () -> T): T {
    return body()
}
非局部返回

調用inline函數

  val l = ReentrantLock()
  lock(l) {
      return
  }

return可使包含該lambda的函數正常退出

調用非inline函數

  val l = ReentrantLock()
  lock(l) {
      return@lock
  }

需要顯示指定標籤返回

crossinline

⼀些內聯函數可能調⽤傳給它們的不是直接來⾃函數體、⽽是來⾃另⼀個執⾏上下⽂的 lambda 表達式參數,例如來⾃局部對象或嵌套函數。在這種情況下,該 lambda 表達式中也不允許⾮局部控制流。爲了標識這種情況,該lambda 表達式參數需要⽤ crossinline 修飾符標記。

上述爲官方解釋,說白了就是不允許直接return返回包含lambda的函數,需顯示指定標籤返回。

var lastTime: Long = 0L

inline fun View.setSingleClick(crossinline onclick: (v: View?) -> Unit) {
    this.setOnClickListener {
        val currentTime = System.currentTimeMillis()
        if (currentTime - lastTime > 500) {
            onclick.invoke(it)
        }
        lastTime = currentTime
    }
}

調用處

  btContent.setSingleClick {
      return@setSingleClick
  }
具體化的類型參數

內聯函數⽀持具體化的類型參數

inline fun <reified T> ktxClass() = T::class.java

通過reified關鍵字修飾泛型,可以直接獲取泛型的類型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章