Kotlin學(xué)習(xí)(九): 擴(kuò)展(Extensions)和This表達(dá)式

Kotlin1.1

擴(kuò)展是Kotlin中特別強(qiáng)大的一個(gè)功能,如擴(kuò)展函數(shù)仰美,本文是學(xué)習(xí)Kotlin中的擴(kuò)展(Extensions)和This表達(dá)式的相關(guān)知識(shí)迷殿。

擴(kuò)展(Extensions)

在Java開(kāi)發(fā)時(shí),會(huì)經(jīng)常將那些共用的方法寫到一個(gè)Utils類咖杂,如FileUtils,StringUtils等等庆寺。很有名的java.util.Collections也是其中一員的,在使用的時(shí)候

Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))

Kotlin中提供了一種可以在不繼承父類诉字,也不使用類似Decorator這樣的設(shè)計(jì)模式的情況下對(duì)指定類進(jìn)行擴(kuò)展懦尝,在Kotlin中稱為擴(kuò)展的特殊聲明,支持函數(shù)擴(kuò)展和屬性擴(kuò)展壤圃。
如上面的可以寫成

list.swap(list.binarySearch(otherList.max()), list.max())

比如要將Toast寫成可以直接調(diào)用toast(this, "toast")

fun Context.toast(context: Context, content: String) {
    Toast.makeText(context, content, Toast.LENGTH_SHORT).show()
}

擴(kuò)展函數(shù)(Extension Functions)

要聲明一個(gè)擴(kuò)展函數(shù)陵霉,我們需要在函數(shù)的名稱前加上一個(gè)接收者類型并且加上.符號(hào)

/**
* MutableList<Int> 添加一個(gè) swap() 擴(kuò)展函數(shù)
*/
fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // this對(duì)應(yīng)list
    this[index1] = this[index2]
    this[index2] = tmp
}

在擴(kuò)展函數(shù)中的this關(guān)鍵字表示接收者對(duì)象,下面會(huì)講到this表達(dá)式伍绳,在這里的this對(duì)應(yīng)MutableList<Int>類型的對(duì)象踊挠。

val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // 在 `swap()` 函數(shù)中 `this` 持有的值是 `l`

如果要改成對(duì)任意類型MutableList<T>都適用

fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] 
    this[index1] = this[index2]
    this[index2] = tmp
}

像在集合Collections里面就可以找到很多的擴(kuò)展函數(shù):

extension

擴(kuò)展是靜態(tài)解析(Extensions are resolved statically)

由于Kotlin的擴(kuò)展是靜態(tài)解析的,所以在定義了一個(gè)擴(kuò)展函數(shù)之后冲杀,并不會(huì)給這個(gè)類添加方法止毕,而是讓這個(gè)方法能夠被這個(gè)類的實(shí)例對(duì)象通過(guò).來(lái)調(diào)用。舉例

open class C

class D: C()

fun C.foo() = "c"

fun D.foo() = "d"

fun printFoo(c: C) {
    println(c.foo())
}

在調(diào)用printFoo(D())的話會(huì)打印c還是d漠趁,答案是打印c扁凛,因?yàn)樵诙x擴(kuò)展函數(shù)的時(shí)候接收對(duì)象類型是C類,而且Kotlin的擴(kuò)展是靜態(tài)解析的闯传,所以即使調(diào)用的時(shí)候是傳了C類的子類D類進(jìn)去谨朝,還是會(huì)執(zhí)行定義的時(shí)候的類型的函數(shù)。

那如果類中有個(gè)函數(shù)foo()甥绿,然后在寫個(gè)擴(kuò)展函數(shù)C.foo()字币,這時(shí)候如果調(diào)用的話,是調(diào)用哪個(gè)函數(shù)呢共缕?

class C {
    fun foo() { println("member") }

}
fun C.foo() { println("extension") }

答案是調(diào)用類的成員函數(shù)洗出,如果有同名同參數(shù)的成員函數(shù)和擴(kuò)展函數(shù),調(diào)用的時(shí)候必然會(huì)使用成員函數(shù)图谷,所以這里會(huì)打印member

擴(kuò)展函數(shù)完全可以重載具有相同名稱但不同參數(shù)的成員函數(shù)

class C {
    fun foo() { println("number") }
}

fun C.foo(i:Int) { println("extention") }

總結(jié)一下翩活,如果擴(kuò)展函數(shù)和成員函數(shù)同名同參數(shù)阱洪,則成員函數(shù)優(yōu)先級(jí)高于擴(kuò)展函數(shù)。

可空接收者(Nullable Receiver)

Kotlin的擴(kuò)展支持接收者對(duì)象為空菠镇,舉個(gè)例子冗荸,在調(diào)用字符串的toString()函數(shù)的時(shí)候,如果字符串為null的話會(huì)報(bào)空指針異常利耍,這里可以改成擴(kuò)展函數(shù)蚌本,里面將傳進(jìn)來(lái)的字符串進(jìn)行this == null判斷,就可以在 Kotlin 中任意調(diào)用toString()函數(shù)而不進(jìn)行空指針檢查隘梨。

fun Any?.toString(): String {
    if (this == null) return "null"
    // 在空檢查之后程癌,`this` 被自動(dòng)轉(zhuǎn)為非空類型,因此 toString() 可以被解析到任何類的成員函數(shù)中
    return toString()
}

屬性擴(kuò)展(Extension Properties)

與函數(shù)類似轴猎,Kotlin支持?jǐn)U展屬性

val <T> List<T>.lastIndex: Int
    get() = size - 1

前面說(shuō)過(guò)嵌莉,Kotlin是靜態(tài)解析擴(kuò)展的,所以擴(kuò)展屬性不會(huì)有備用字段税稼,這也是為什么初始化函數(shù)不允許有擴(kuò)展屬性,擴(kuò)展屬性只能夠通過(guò)明確提供gettersetter函數(shù)來(lái)進(jìn)行定義垮斯。

val Foo.bar = 1 // 錯(cuò)誤: 初始化函數(shù)不允許有擴(kuò)展屬性

伴生對(duì)象擴(kuò)展(Companion Object Extensions)

如果一個(gè)類有伴生對(duì)象郎仆,同樣可以為伴隨對(duì)象定義擴(kuò)展函數(shù)和屬性

class MyClass {
    companion object { } 
}

fun MyClass.Companion.foo() {
    // ...
}

調(diào)用的時(shí)候

MyClass.foo()

擴(kuò)展的范圍

在前面說(shuō)過(guò)Top-level是與類同級(jí)的,大多數(shù)情況下兜蠕,擴(kuò)展也可以在Top-level層級(jí)定義扰肌。

package foo.bar
fun Baz.goo() { ... }

在使用的時(shí)候與一般類使用一樣,要import導(dǎo)入包熊杨。

package com.example,usage

import foo.bar.goo // 導(dǎo)入所有名字叫 "goo" 的擴(kuò)展
// 或者
import foo.bar.* // 導(dǎo)入foo.bar包下得所有數(shù)據(jù)

fun usage(baz: Baz) {
    baz.goo()
}

This表達(dá)式(This Expression)

上面說(shuō)了曙旭,在擴(kuò)展函數(shù)中的this關(guān)鍵字對(duì)應(yīng)接收者對(duì)象,this表示當(dāng)前接收者晶府,用到了this表達(dá)式桂躏。

  • 在類的成員中,this表示當(dāng)前類的對(duì)象
  • 在擴(kuò)展函數(shù)或帶接收者的文本函數(shù)中川陆,this.的左邊剂习,表示接收者參數(shù)

This表達(dá)式的限制(Qualified this)

為了在范圍外部(類、擴(kuò)展函數(shù)较沪、帶接收者的文本函數(shù)訪問(wèn)this鳞绕,需要使用到@label,在前面也說(shuō)過(guò)return到標(biāo)簽(@label)尸曼,這里用this@label表示

class A { // 默認(rèn)有@A標(biāo)簽
    // this表示A
    val aThis = this 
    inner class B { // 默認(rèn)有@B標(biāo)簽
        // this表示B
        val bThis = this 
        fun Int.foo() { // 默認(rèn)有@foo標(biāo)簽
            // 表示A
            val a = this@A 
            // 表示B
            val b = this@B 

            // this表示帶接收者為Int的文本函數(shù)foo()
            val c = this 
            // 表示帶接收者為Int的文本函數(shù)foo()
            val c1 = this@foo 
        }
    }
}

將下面的代碼運(yùn)行们何,會(huì)打印出什么呢?

運(yùn)行的結(jié)果為:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末控轿,一起剝皮案震驚了整個(gè)濱河市冤竹,隨后出現(xiàn)的幾起案子拂封,更是在濱河造成了極大的恐慌,老刑警劉巖贴见,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烘苹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡片部,警方通過(guò)查閱死者的電腦和手機(jī)镣衡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)档悠,“玉大人廊鸥,你說(shuō)我怎么就攤上這事∠剿” “怎么了惰说?”我有些...
    開(kāi)封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)缘回。 經(jīng)常有香客問(wèn)我吆视,道長(zhǎng),這世上最難降的妖魔是什么酥宴? 我笑而不...
    開(kāi)封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任啦吧,我火速辦了婚禮,結(jié)果婚禮上拙寡,老公的妹妹穿的比我還像新娘授滓。我一直安慰自己,他們只是感情好肆糕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布般堆。 她就那樣靜靜地躺著,像睡著了一般诚啃。 火紅的嫁衣襯著肌膚如雪淮摔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天始赎,我揣著相機(jī)與錄音噩咪,去河邊找鬼。 笑死极阅,一個(gè)胖子當(dāng)著我的面吹牛胃碾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筋搏,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼仆百,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了奔脐?” 一聲冷哼從身側(cè)響起俄周,我...
    開(kāi)封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吁讨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后峦朗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體建丧,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年波势,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了翎朱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尺铣,死狀恐怖拴曲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凛忿,我是刑警寧澤澈灼,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站店溢,受9級(jí)特大地震影響叁熔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜床牧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一荣回、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叠赦,春花似錦驹马、人聲如沸革砸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)算利。三九已至册踩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間效拭,已是汗流浹背暂吉。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缎患,地道東北人慕的。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像挤渔,于是被迫代替她去往敵國(guó)和親肮街。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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