使用高階函數(shù)會(huì)帶來(lái)一些運(yùn)行時(shí)的效率損失:每一個(gè)函數(shù)都是一個(gè)對(duì)象唠叛,且這個(gè)函數(shù)對(duì)象捕獲了一個(gè)閉包浪箭。也就是說(shuō)控漠,閉包內(nèi)的變量可以在函數(shù)對(duì)象內(nèi)部訪問(wèn)。內(nèi)存分配(為函數(shù)對(duì)象和類)和實(shí)際調(diào)用將引入運(yùn)行時(shí)間開(kāi)銷全肮。
但通過(guò)使用內(nèi)聯(lián)λ表達(dá)式敞咧,可以避免這些情況的出現(xiàn)。下面的函數(shù)是這種情況的很好例子辜腺。lock()
函數(shù)可以非常容易的在調(diào)用處被內(nèi)聯(lián)休建≌Э郑考慮下面的情況:
lock(l) { foo() }
編譯器沒(méi)有為參數(shù)創(chuàng)建一個(gè)函數(shù)對(duì)象并生成一個(gè)調(diào)用。而是生成了如下代碼:
l.lock()
try {
foo()
}
finally {
l.unlock()
}
這不就是我們一開(kāi)始想要的测砂?
為了讓編譯器這么做茵烈,我們需要使用inline
修飾符來(lái)標(biāo)記lock()
函數(shù):
inline fun lock<T>(lock: Lock, body: () -> T): T {
// ...
}
這個(gè)inline修飾符將影響函數(shù)本身和傳給給函數(shù)的λ表達(dá)式:所有這些都將內(nèi)聯(lián)到調(diào)用處。
內(nèi)聯(lián)可能導(dǎo)致生成代碼的增加砌些,但如果我們使用得當(dāng)(不內(nèi)聯(lián)大函數(shù))呜投,它將在性能上有所提升,尤其是在循環(huán)內(nèi)部的超多態(tài)調(diào)用處存璃。
非內(nèi)聯(lián)(noinline)
如果你只想傳入內(nèi)聯(lián)函數(shù)中的一部分λ表達(dá)式被內(nèi)聯(lián)仑荐,你可以使用noinline
修飾符修飾那些不想被內(nèi)聯(lián)的形參:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ...
}
內(nèi)聯(lián)λ表達(dá)式只能在內(nèi)聯(lián)函數(shù)內(nèi)部調(diào)用,或者作為內(nèi)聯(lián)參數(shù)傳遞有巧,但是那些被noinline修飾符標(biāo)記的參數(shù)則可以按我們?nèi)魏蜗胗玫姆绞讲僮鳎捍鎯?chǔ)在字段中释漆,或傳遞悲没。
注意:如果一個(gè)內(nèi)聯(lián)函數(shù)沒(méi)有可內(nèi)聯(lián)的函數(shù)參數(shù)篮迎,且沒(méi)有具體化類型的參數(shù),編譯器將產(chǎn)生警告示姿,因?yàn)閮?nèi)聯(lián)這樣的函數(shù)很可能沒(méi)有益處(若你認(rèn)為內(nèi)聯(lián)是必須的甜橱,則可以關(guān)閉該警告)。
非局部返回(Non-local returns)
在Kotlin中栈戳,我們可以使用一個(gè)常規(guī)的岂傲,沒(méi)有任何修飾符的return
語(yǔ)句來(lái)返回一個(gè)有名函數(shù)或匿名函數(shù)。這以為要想只返回一個(gè)λ表達(dá)式子檀,我們必須使用標(biāo)簽镊掖,在λ表達(dá)式禁止使用單獨(dú)一個(gè)return
語(yǔ)句,因?yàn)棣吮磉_(dá)式不能使包含它的封閉函數(shù)返回:
fun foo() {
ordinaryFunction {
return // ERROR: can not make `foo` return here
}
}
但是如果傳入函數(shù)的λ表達(dá)式是內(nèi)聯(lián)的褂痰,則該return也可以內(nèi)聯(lián)亩进,如下是允許的:
fun foo() {
inlineFunction {
return // OK: the lambda is inlined
}
}
這種返回(位于λ表達(dá)式中,但退出的是包含該λ表達(dá)式的封閉函數(shù))稱為非局部返回缩歪。我們習(xí)慣了在循環(huán)中使用這種結(jié)構(gòu)归薛,循環(huán)內(nèi)部通常置入內(nèi)聯(lián)函數(shù):
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
if (it == 0) return true // returns from hasZeros
}
return false
}
注意:一些內(nèi)聯(lián)函數(shù)有可能調(diào)用的作為其參數(shù)傳入的λ表達(dá)式不直接在函數(shù)體中,而是執(zhí)行另一個(gè)上下文的λ表達(dá)式匪蝙,例如一個(gè)局部對(duì)象或一個(gè)嵌套函數(shù)主籍。這是,非局部返回在這種λ表達(dá)式將不能使用逛球。為了說(shuō)明這種情況千元,λ表達(dá)式參數(shù)需要以crossinline
修飾符標(biāo)記:
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ...
}
break和continue在內(nèi)聯(lián)λ表達(dá)式中不允許使用,但我們正在計(jì)劃支持它們颤绕。
具體類型參數(shù)(Reified type parameters)
有時(shí)候我們需要訪問(wèn)一個(gè)類型幸海,這個(gè)類型作為參數(shù)傳遞給我們:
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p.parent
}
@Suppress("UNCHECKED_CAST")
return p as T?
}
我們向上遍歷一棵樹(shù)并檢查每一個(gè)節(jié)點(diǎn)是否是指定類型蜡歹。但方法的調(diào)用則不是很美觀直接:
treeNode.findParentOfType(MyTreeNode::class.java)
事實(shí)上我們僅想傳遞一個(gè)類型給這個(gè)函數(shù),就像這樣:
treeNode.findParentOfType<MyTreeNode>()
為了能夠這么做涕烧,內(nèi)聯(lián)函數(shù)支持具體化的類型參數(shù)月而,因此我們可以這樣寫(xiě):
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
我們使用reified
修飾符來(lái)修飾類型參數(shù),這時(shí)候可以在函數(shù)內(nèi)部訪問(wèn)類型參數(shù)议纯,就像是一個(gè)普通的類父款。由于函數(shù)是內(nèi)聯(lián)的,不需要反射瞻凤,常規(guī)操作符如憨攒!is
和as
都可以正常使用。現(xiàn)在我們可以如下調(diào)用上個(gè)函數(shù):
myTree.findParentOfType<MyTreeNodeType>()
雖然在需要情況下不需要反射阀参,我們?nèi)匀豢梢詫?duì)一個(gè)具體化的類型參數(shù)使用它:
inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>) {
println(membersOf<StringBuilder>().joinToString("\n"))
}
普通函數(shù)(未被inline標(biāo)記)不能有具體化參數(shù)肝集。不具有運(yùn)行時(shí)表示的類型(例如非具體化的類型參數(shù)或類似于Nothing的虛構(gòu)類型)不能用作具體化的類型參數(shù)的實(shí)參。
內(nèi)聯(lián)屬性(Inline properties (since 1.1))
修飾符inline可以用于沒(méi)有后背字段的屬性的訪問(wèn)器蛛壳⌒诱埃可以單獨(dú)注解屬性訪問(wèn)器:
val foo: Foo
inline get() = Foo()
var bar: Bar
get() = ...
inline set(v) { ... }
也可以注解一個(gè)睡醒,將兩個(gè)訪問(wèn)器都標(biāo)記為內(nèi)聯(lián):
inline var bar: Bar
get() = ...
set(v) { ... }
在調(diào)用處衙荐,內(nèi)聯(lián)訪問(wèn)將被作為常規(guī)內(nèi)聯(lián)函數(shù)一樣被內(nèi)聯(lián)捞挥。