使用高階函數時,每個函數都是一個對象,函數調用時還有入棧出棧的開銷。
以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關鍵字修飾泛型,可以直接獲取泛型的類型。