Kotlin基礎(chǔ)-擴(kuò)展

一立砸、定義

Kotlin 在不修改類 / 不繼承類的情況下,向一個(gè)類添加新函數(shù)或者新屬性颗祝,更符合開閉原則。
擴(kuò)展是一種靜態(tài)行為恼布,對(duì)被擴(kuò)展的類代碼本身不會(huì)造成任何影響螺戳。

  • 擴(kuò)展屬性:定義在類或者kotlin文件中桥氏,不允許定義在函數(shù)中;
  • 擴(kuò)展函數(shù):擴(kuò)展函數(shù)可以在已有類中添加新的方法字支,不會(huì)對(duì)原類做修改凤藏,擴(kuò)展函數(shù)定義形式:
fun receiverType.functionName(params){
    body
}
//receiverType:表示函數(shù)的接收者,也就是函數(shù)擴(kuò)展的對(duì)象
//functionName:擴(kuò)展函數(shù)的名稱
//params:擴(kuò)展函數(shù)的參數(shù)揖庄,可以為NULL

擴(kuò)展的本質(zhì):擴(kuò)展函數(shù)是定義在類外部的靜態(tài)函數(shù),函數(shù)的第一個(gè)參數(shù)是接收者類型的對(duì)象欠雌。這意味著調(diào)用擴(kuò)展時(shí)不會(huì)創(chuàng)建適配對(duì)象或者任何運(yùn)行時(shí)的額外消耗蹄梢。

二富俄、擴(kuò)展函數(shù)

1、擴(kuò)展函數(shù)是靜態(tài)解析的霍比,并不是接收者類型的虛擬成員

在調(diào)用擴(kuò)展函數(shù)時(shí)幕袱,具體被調(diào)用的的是哪一個(gè)函數(shù),由調(diào)用函數(shù)的的對(duì)象表達(dá)式來(lái)決定的们豌,而不是動(dòng)態(tài)的類型決定的:

open class C

class D: C()

fun C.foo() = "c"   // 擴(kuò)展函數(shù) foo

fun D.foo() = "d"   // 擴(kuò)展函數(shù) foo

fun printFoo(c: C) {
    println(c.foo())  // 類型是 C 類
}

fun main(arg:Array<String>){
    printFoo(D())
}
//實(shí)際輸出c
2、若擴(kuò)展函數(shù)和成員函數(shù)一致浅妆,則使用該函數(shù)時(shí),會(huì)優(yōu)先使用成員函數(shù)凌外。
3辩尊、擴(kuò)展一個(gè)空對(duì)象

在擴(kuò)展函數(shù)內(nèi), 可以通過(guò) this 來(lái)判斷接收者是否為 NULL,這樣对省,即使接收者為 NULL,也可以調(diào)用擴(kuò)展函數(shù)蝗拿。

fun Any?.toString(): String {
    if (this == null) return "null"
    // 空檢測(cè)之后晾捏,“this”會(huì)自動(dòng)轉(zhuǎn)換為非空類型,所以下面的 toString()
    // 解析為 Any 類的成員函數(shù)
    return toString()
}

三惦辛、擴(kuò)展屬性

擴(kuò)展屬性允許定義在類或者kotlin文件中劳秋,不允許定義在函數(shù)中。初始化屬性因?yàn)閷傩詻](méi)有后端字段(backing field)玻淑,所以不允許被初始化,只能由顯式提供的 getter/setter 定義呀伙。

val Foo.bar = 1 // 錯(cuò)誤:擴(kuò)展屬性不能有初始化器

擴(kuò)展屬性只能被聲明為 val

四补履、伴生對(duì)象的擴(kuò)展

如果一個(gè)類定義有一個(gè)伴生對(duì)象 剿另,你也可以為伴生對(duì)象定義擴(kuò)展函數(shù)和屬性。
伴生對(duì)象通過(guò)"類名."形式調(diào)用伴生對(duì)象雨女,伴生對(duì)象聲明的擴(kuò)展函數(shù)谚攒,通過(guò)用類名限定符來(lái)調(diào)用:

class MyClass {
    companion object { }  // 將被稱為 "Companion"
}

fun MyClass.Companion.foo() {
    println("伴隨對(duì)象的擴(kuò)展函數(shù)")
}

val MyClass.Companion.no: Int
    get() = 10

伴生對(duì)象內(nèi)的成員相當(dāng)于 Java 中的靜態(tài)成員,其生命周期伴隨類始終馏臭,在伴生對(duì)象內(nèi)部可以定義變量和函數(shù),這些變量和函數(shù)可以直接用類名引用讼稚。
對(duì)于伴生對(duì)象擴(kuò)展函數(shù)括儒,有兩種形式锐想,一種是在類內(nèi)擴(kuò)展,一種是在類外擴(kuò)展痛倚,這兩種形式擴(kuò)展后的函數(shù)互不影響(甚至名稱都可以相同)规婆,即使名稱相同,它們也完全是兩個(gè)不同的函數(shù)抒蚜,并且有以下特點(diǎn):
(1)類內(nèi)擴(kuò)展的伴隨對(duì)象函數(shù)和類外擴(kuò)展的伴隨對(duì)象可以同名,它們是兩個(gè)獨(dú)立的函數(shù)耘戚,互不影響嗡髓;
(2)當(dāng)類內(nèi)擴(kuò)展的伴隨對(duì)象函數(shù)和類外擴(kuò)展的伴隨對(duì)象同名時(shí)收津,類內(nèi)的其它函數(shù)優(yōu)先引用類內(nèi)擴(kuò)展的伴隨對(duì)象函數(shù)浊伙,即對(duì)于類內(nèi)其它成員函數(shù)來(lái)說(shuō),類內(nèi)擴(kuò)展屏蔽類外擴(kuò)展长捧;
(3)類內(nèi)擴(kuò)展的伴隨對(duì)象函數(shù)只能被類內(nèi)的函數(shù)引用嚣鄙,不能被類外的函數(shù)和伴隨對(duì)象內(nèi)的函數(shù)引用串结;
(4)類外擴(kuò)展的伴隨對(duì)象函數(shù)可以被伴隨對(duì)象內(nèi)的函數(shù)引用哑子;

五肌割、擴(kuò)展的作用域

通常擴(kuò)展函數(shù)或?qū)傩远x在頂級(jí)包下;
要使用所定義包之外的一個(gè)擴(kuò)展, 通過(guò)import導(dǎo)入擴(kuò)展的函數(shù)名進(jìn)行使用;

六、擴(kuò)展聲明為成員

  • 在一個(gè)類內(nèi)部你可以為另一個(gè)類聲明擴(kuò)展把敞。
  • 在這個(gè)擴(kuò)展中弥奸,有個(gè)多個(gè)隱含的接受者,其中擴(kuò)展方法定義所在類的實(shí)例稱為分發(fā)接受者奋早,而擴(kuò)展方法的目標(biāo)類型的實(shí)例稱為擴(kuò)展接受者。
  • 以成員的形式定義的擴(kuò)展函數(shù), 可以聲明為 open , 而且可以在子類中覆蓋. 也就是說(shuō), 在這類擴(kuò)展函數(shù)的派 發(fā)過(guò)程中, 針對(duì)分發(fā)接受者是虛擬的(virtual), 但針對(duì)擴(kuò)展接受者仍然是靜態(tài)的伸蚯。
open class D {
}

class D1 : D() {
}

open class C {
    open fun D.foo() {
        println("D.foo in C")
    }

    open fun D1.foo() {
        println("D1.foo in C")
    }

    fun caller(d: D) {
        d.foo()   // 調(diào)用擴(kuò)展函數(shù)
    }
}

class C1 : C() {
    override fun D.foo() {
        println("D.foo in C1")
    }

    override fun D1.foo() {
        println("D1.foo in C1")
    }
}


fun main(args: Array<String>) {
    C().caller(D())   // 輸出 "D.foo in C"
    C1().caller(D())  // 輸出 "D.foo in C1" —— 分發(fā)接收者虛擬解析
    C().caller(D1())  // 輸出 "D.foo in C" —— 擴(kuò)展接收者靜態(tài)解析
}


//實(shí)例執(zhí)行輸出結(jié)果為:
//D.foo in C
//D.foo in C1
//D.foo in C

七摩渺、內(nèi)置擴(kuò)展函數(shù)

擴(kuò)展函數(shù)是 Kotlin 用于簡(jiǎn)化一些代碼的書寫產(chǎn)生的,其中有 五個(gè)函數(shù)

  • let
  • with
  • run
  • apply
  • also

1摇幻、let 函數(shù)

  1. 在函數(shù)塊內(nèi)可以通過(guò) it 指代該對(duì)象。
  2. 返回值為函數(shù)塊的最后一行或指定return表達(dá)式挥萌。
//一般寫法
val result = "hello".let {
  println(it.length)
  1000
}

使用場(chǎng)景:
1绰姻、使用let函數(shù)處理需要針對(duì)一個(gè)可null的對(duì)象統(tǒng)一做判空處理引瀑;
2狂芋、又或者是需要去明確一個(gè)變量所處特定的作用域范圍內(nèi)可以使用憨栽;

2帜矾、with 函數(shù)

  1. 前面的幾個(gè)函數(shù)使用方式略有不同,因?yàn)樗皇且詳U(kuò)展的形式存在的屡萤。它是將某對(duì)象作為函數(shù)的參數(shù),在函數(shù)塊內(nèi)可以通過(guò) this 指代該對(duì)象掸宛。
  2. 返回值為函數(shù)塊的最后一行或指定return表達(dá)式死陆。
//一般寫法
var result = with(Person()) {
    println(this.name + this.age)
    1000
}

使用場(chǎng)景:
1唧瘾、適用于調(diào)用同一個(gè)類的多個(gè)方法時(shí)别凤,可以省去類名重復(fù),直接調(diào)用類的方法即可领虹,經(jīng)常用于Android中RecyclerView中onBinderViewHolder中规哪,數(shù)據(jù)model的屬性映射到UI上

3、run 函數(shù)

  1. 實(shí)際上可以說(shuō)是let和with兩個(gè)函數(shù)的結(jié)合體由缆,run函數(shù)只接收一個(gè)lambda函數(shù)為參數(shù)注祖,以閉包形式返回猾蒂。
  2. 返回值為最后一行的值或者指定的return的表達(dá)式。
var result = person.run {
    println("$name + $age")
    1000
}

使用場(chǎng)景:
1是晨、適用于let,with函數(shù)任何場(chǎng)景。
因?yàn)閞un函數(shù)是let,with兩個(gè)函數(shù)結(jié)合體蚊逢,準(zhǔn)確來(lái)說(shuō)它彌補(bǔ)了let函數(shù)在函數(shù)體內(nèi)必須使用it參數(shù)替代對(duì)象,在run函數(shù)中可以像with函數(shù)一樣可以省略箫章,直接訪問(wèn)實(shí)例的公有屬性和方法,另一方面它彌補(bǔ)了with函數(shù)傳入對(duì)象判空問(wèn)題檬寂,在run函數(shù)中可以像let函數(shù)一樣做判空處理

4、apply 函數(shù)

  1. 從結(jié)構(gòu)上來(lái)看apply函數(shù)和run函數(shù)很像女蜈,唯一不同點(diǎn)就是它們各自返回的值不一樣
  2. 返回值是傳入對(duì)象的本身
val person = Person().apply {
    name = "liming"
    age = 50
}

使用場(chǎng)景:
整體作用功能和run函數(shù)很像持舆,唯一不同點(diǎn)就是它返回的值是對(duì)象本身伪窖,而run函數(shù)是一個(gè)閉包形式返回聊倔,返回的是最后一行的值。正是基于這一點(diǎn)差異它的適用場(chǎng)景稍微與run函數(shù)有點(diǎn)不一樣孤荣。
apply一般用于一個(gè)對(duì)象實(shí)例初始化的時(shí)候甸陌,需要對(duì)對(duì)象中的屬性進(jìn)行賦值盐股。或者動(dòng)態(tài)inflate出一個(gè)XML的View的時(shí)候需要給View綁定數(shù)據(jù)也會(huì)用到疯汁,這種情景非常常見牲尺。特別是在我們開發(fā)中會(huì)有一些數(shù)據(jù)model向View model轉(zhuǎn)化實(shí)例化的過(guò)程中需要用到

5、also 函數(shù)

  1. also函數(shù)的結(jié)構(gòu)實(shí)際上和let很像唯一的區(qū)別就是返回值的不一樣谤碳,let是以閉包的形式返回,返回函數(shù)體內(nèi)最后一行的值溢豆,如果最后一行為空就返回一個(gè)Unit類型的默認(rèn)值蜒简。
  2. 返回值是傳入對(duì)象的本身。
val result = "hello".also {
    println(it.length)
}

使用場(chǎng)景:
適用于let函數(shù)的任何場(chǎng)景漩仙,also函數(shù)和let很像,只是唯一的不同點(diǎn)就是let函數(shù)最后的返回值是最后一行的返回值而also函數(shù)的返回值是返回當(dāng)前的這個(gè)對(duì)象讯赏。一般可用于多個(gè)擴(kuò)展函數(shù)鏈?zhǔn)秸{(diào)用垮兑。

區(qū)別

方法名 是否有it 是否有this return類型
let ? × 最后一句
also ? × this
apply × ? this
run × ? 最后一句
with × ? 最后一句
為什么run漱挎、with系枪、apply用this磕谅,also 和 let 用it私爷?
  • run、with衬浑、apply 函數(shù)中的參數(shù) block 是 「T 的擴(kuò)展函數(shù)」,所以采用 this 是擴(kuò)展函數(shù)的接收者對(duì)象(receiver)放刨。另外因?yàn)?block 沒(méi)有參數(shù)工秩,所以不存在 it 的定義。
  • also 和 let 參數(shù) block 是 「參數(shù)為 T 的函數(shù)」助币,所以采用 it 是唯一參數(shù)(argument)浪听。另外因?yàn)?block 不是擴(kuò)展函數(shù)眉菱,所以不存在 this 的定義迹栓。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末俭缓,一起剝皮案震驚了整個(gè)濱河市克伊,隨后出現(xiàn)的幾起案子华坦,更是在濱河造成了極大的恐慌,老刑警劉巖季春,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洗搂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡载弄,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門宇攻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人倡勇,你說(shuō)我怎么就攤上這事逞刷∑扌埽” “怎么了夸浅?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵扔役,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我亿胸,道長(zhǎng)坯钦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任侈玄,我火速辦了婚禮婉刀,結(jié)果婚禮上序仙,老公的妹妹穿的比我還像新娘突颊。我一直安慰自己,他們只是感情好律秃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著友绝,像睡著了一般堤尾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迁客,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音掷漱,去河邊找鬼粘室。 笑死,一個(gè)胖子當(dāng)著我的面吹牛卜范,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播海雪,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锦爵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了奥裸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤湾宙,失蹤者是張志新(化名)和其女友劉穎樟氢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侠鳄,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年伟恶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碴开。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叹螟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出台盯,到底是詐尸還是另有隱情罢绽,我是刑警寧澤静盅,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布良价,位于F島的核電站寝殴,受9級(jí)特大地震影響明垢,放射性物質(zhì)發(fā)生泄漏蚣常。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一抵蚊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溯革,春花似錦贞绳、人聲如沸致稀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)萎攒。三九已至,卻和暖如春耍休,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔑歌。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工羹应, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人次屠。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓雳刺,卻偏偏與公主長(zhǎng)得像劫灶,于是被迫代替她去往敵國(guó)和親掖桦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子本昏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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