Kotlin進(jìn)階之內(nèi)聯(lián)函數(shù)(Inline Functions)

使用高階函數(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ī)操作符如憨攒!isas都可以正常使用。現(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)捞挥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市忧吟,隨后出現(xiàn)的幾起案子砌函,更是在濱河造成了極大的恐慌,老刑警劉巖溜族,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讹俊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡煌抒,警方通過(guò)查閱死者的電腦和手機(jī)仍劈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摧玫,“玉大人耳奕,你說(shuō)我怎么就攤上這事∥芟瘢” “怎么了屋群?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坏挠。 經(jīng)常有香客問(wèn)我芍躏,道長(zhǎng),這世上最難降的妖魔是什么降狠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任对竣,我火速辦了婚禮庇楞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘否纬。我一直安慰自己吕晌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布临燃。 她就那樣靜靜地躺著睛驳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪膜廊。 梳的紋絲不亂的頭發(fā)上乏沸,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音爪瓜,去河邊找鬼蹬跃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛铆铆,可吹牛的內(nèi)容都是我干的蝶缀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼算灸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼扼劈!你這毒婦竟也來(lái)了驻啤?” 一聲冷哼從身側(cè)響起菲驴,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎骑冗,沒(méi)想到半個(gè)月后赊瞬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贼涩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年巧涧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遥倦。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谤绳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出袒哥,到底是詐尸還是另有隱情缩筛,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布堡称,位于F島的核電站瞎抛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏却紧。R本人自食惡果不足惜桐臊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一胎撤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧断凶,春花似錦伤提、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至砚著,卻和暖如春次伶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稽穆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工冠王, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人舌镶。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓柱彻,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親餐胀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哟楷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容