每天學(xué)一點(diǎn) Kotlin -- 函數(shù):擴(kuò)展

----《第一季Kotlin崛起:次世代Android開發(fā) 》學(xué)習(xí)筆記

總目錄:每天學(xué)一點(diǎn) Kotlin ---- 目錄
上一篇:每天學(xué)一點(diǎn) Kotlin -- 函數(shù):操作符
下一篇:每天學(xué)一點(diǎn) Kotlin -- 函數(shù):字面量

1. 函數(shù)擴(kuò)展

1.1 由來:有時候很想對現(xiàn)有的類進(jìn)行改進(jìn),加入新的函數(shù),但是苦于無權(quán)限更改或者沒有源代碼。有一種方法就是繼承這個類,并在子類實(shí)現(xiàn)上增加函數(shù),但是這個方法并不總是有效。因?yàn)橛行╊惪梢砸呀?jīng)是 final 不可能繼承的。而且沒法對既有類型的實(shí)例進(jìn)行控制。另一種方法是單獨(dú)新建一個文件,把已有類型的實(shí)例放進(jìn)自定義的函數(shù)里解決,從而行形成一個工具類或幫助類。但是這樣層層的封裝在工程量比較大的時候也并不是很方便。

1.2 雖然 Kotlin 根 Java 語法不同,但是和 Java 一樣還是面向?qū)ο缶幊痰乃枷霝橹???墒菍ι厦嫣岬降膯栴}面向?qū)ο缶幊桃膊荒芎芎玫亟鉀Q時,這時候擴(kuò)展就很有用了。

1.3 擴(kuò)展有很多種,其中函數(shù)擴(kuò)展是比較常用的,擴(kuò)展函數(shù)就像頂層函數(shù)一樣定義。 舉個栗子:

fun Int.square() = this * this
3.square()

1.4 如上所示,定義函數(shù)擴(kuò)展也很簡單,只要在函數(shù)名前面加上類型和點(diǎn)。這個被擴(kuò)張的類型,稱為接收者類型。也就是說這個接收者類型被進(jìn)行了函數(shù)擴(kuò)展。this 代表類型實(shí)例的引用。

2. 擴(kuò)展函數(shù)的優(yōu)先級

2.1 擴(kuò)展函數(shù)不能重載類或接口中已經(jīng)定義的函數(shù)。如果你定義了一個與既有函數(shù)一模一樣的擴(kuò)展函數(shù),名字一樣,參數(shù)一樣,參數(shù)的順序一樣,返回值一樣,這個擴(kuò)張函數(shù)就是無效的。能重載那就不是擴(kuò)展了,那是子類繼承。因?yàn)樵诰幾g過程中,編譯器會首先去接收者類型的所有父類和接口中去查找有沒有相同的成員函數(shù),如果有,則使用。
2.2 因此在做擴(kuò)展函數(shù)之前,盡量確保類型中無此功能的函數(shù),名字也盡量起的個人化一點(diǎn)。

3. 擴(kuò)展函數(shù)的作用范圍

3.1 通常用頂層函數(shù)做擴(kuò)展,但也可以在類中擴(kuò)展,這樣在想限制擴(kuò)展的范圍時有用:

class MyNumber(var k: Int) {
    private fun Int.triple() = this * this * this  
    // Int.triple() 只能在MyNumber類之內(nèi)生效,之外是無法使用的
    
    fun addFactor(p: Int) {
        k += p.triple()
    }
}

4. 擴(kuò)展函數(shù)在子類中的重載

4.1 對父類或接口中的同名擴(kuò)展是無效的,但是子類可以重載成員擴(kuò)展函數(shù),前提是這個類是 open 的,即可以重載的。在這種情況下,子類的函數(shù)接收者類型是由運(yùn)行時的實(shí)例決定的,而擴(kuò)展的接收者類型始終是編譯時就決定的,也就是靜態(tài)的。舉個栗子:

open class Particle()

class Electron : Particle()

open class Element(val name: String) {
    open fun Particle.react(name: String): Unit {
        println("$name 與粒子發(fā)生反應(yīng)")
    }

    open fun Electron.react(name: String): Unit {
        println("$name 與電子發(fā)生反應(yīng)生成同位素")
    }

    fun react(particle: Particle): Unit {
        particle.react(name)
    }
}

class NobleGas(name: String) : Element(name) {
    override fun Particle.react(name: String): Unit {
        println("$name 是稀有氣體,不與粒子發(fā)生反應(yīng)")
    }

    override fun Electron.react(name: String): Unit {
        println("$name 是稀有氣體,不與電子發(fā)生反應(yīng)")
    }

    fun react(particle: Electron): Unit {
        particle.react(name)
    }
}

//在main函數(shù)中調(diào)用:
fun main(args: Array<String>) {
    val al = Element("鋁")
    al.react(Particle())
    al.react(Electron())

    val neon = NobleGas("氬")
    neon.react(Particle())
    neon.react(Electron())
}

打印結(jié)果:

鋁 與粒子發(fā)生反應(yīng)
鋁 與粒子發(fā)生反應(yīng)
氬 是稀有氣體,不與粒子發(fā)生反應(yīng)
氬 是稀有氣體,不與電子發(fā)生反應(yīng)

5. infix 中綴函數(shù)

5.1 中綴函數(shù)跟賦值操作符有點(diǎn)像,不同的是名稱可以是任意的。
5.2 舉個栗子:Kotlin 中自帶的 to 函數(shù)可以把兩個變量湊成一個二元組(Pair):

fun testInfix() {
    val train = "北京" to "上海"
    println(train.first)
    println(train.second)
}

5.3 在函數(shù)關(guān)鍵字 fun 前面添加 infix 關(guān)鍵字就可以實(shí)現(xiàn)自定義中綴函數(shù),而且只有一個入?yún)?/em>。

5.4 Kotlin 中可以把成員函數(shù)定義成中綴函數(shù)。因?yàn)橹芯Y函數(shù)是二元的,必須有兩個參數(shù),第一個參數(shù)是實(shí)例,第二個是函數(shù)的入?yún)?。舉個栗子:

infix fun String.到(destination: String): String {
    return "這是從 -" + this + "- 開往 -" + destination + "- 的火車"
}

fun main() {
    val str1 = "北京" 到 ("上海")
    val str2 = "北京" 到 "上海"
    println(str1)
    println(str2)
}

打印結(jié)果:

這是從 -北京- 開往 -上海- 的火車
這是從 -北京- 開往 -上海- 的火車
相關(guān)代碼:https://gitee.com/fzq.com/test-demo
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容