kotlin類與對象——5.擴展

  • 擴展函數
    被擴展的類.函數名
    擴展函數的調用和類中的普通函數一樣
  • 擴展是靜態解析的
    擴展不能真正修改他們所擴展的類,並沒有在一個類中插入一個新成員,僅僅是可以通過該類型的變量用點表達式去調用這個函數
    擴展函數是靜態分發的,不是根據接受者類型的虛方法。這意味着調用的擴展函數是由函數調用所在的表達式的類型來決定的,而不是由表達式運行時求職結果決定的。
open class Person

class Student : Person()

fun Person.getName() = "Person"
fun Student.getName() = "Student"

fun printClassName(p: Person) {
    println(p.getName())
}

fun main(args: Array<String>) {
//得到的結果是Person,因爲調用的擴展函數只取決於參數p聲明的類型,該類型是Person
    printClassName(Student())
}
  • 如果一個類定義了一個成員函數和一個擴展函數,這兩個函數又有相同的接受者類型、相同的名字,並且都適用給定的參數,這種情況總是取成員函數
  • 當然,擴展函數重載相同名字但不同簽名成員函數也完全可以:
class Person {
    fun show() {
        println("show")
    }
}

fun Person.show(i: Int) {
    println("show$i")
}

fun main(args: Array<String>) {
//結果爲1
    Person().show(1)
}
  • 可空接受者
    可以爲可空的接受者類型定義擴展,這樣的擴展可以在對象變量上調用,即時其值爲null,檢查在擴展函數內發生
fun Any?.toString(): String {
    if (this == null) return "null"
    // 空檢測之後,“this”會自動轉換爲非空類型,所以下面的 toString()
    // 解析爲 Any 類的成員函數
    return toString()
}

fun main(args: Array<String>) {
    null.toString()
}
  • 擴展屬性
val String.o: Int get() = 1

==擴展函數沒有實際的將成員插入類中,因此對擴展函數來說幕後字段(如果屬性至少一個訪問器使用默認實現,或者自定義訪問器通過field引用幕後字段,將會爲該屬性生成一個幕後字段)是無效的。這就是爲什麼擴展屬性不能有初始化器,他們的行爲只能由顯示提供的gettter/setter定義

val String.o: Int= 1//這是錯誤的,擴展屬性不能有初始化器(就是直接賦值)
  • 伴生對象的擴展
    如果一個類定義有一個伴生對象,也可以爲伴生對象定義擴展函數與屬性
class Person {
    companion object {}
}

fun Person.Companion.test() {
    println("伴生對象定義擴展函數")
}

fun main(args: Array<String>) {
    Person.test()
}
  • 擴展的作用域
    大多數時候我們在頂層定義擴展(直接在包裏),要使用所定義包之外的一個擴展,需要進行導入
  • 擴展聲明爲成員
    在一個類內部可以爲另一個類聲明擴展。在這樣的擴展內部,有多個隱式接受者(其中的對象成員可以無需通過限定符訪問)。擴展聲明所在的類的實例稱爲分發接受者,擴展方法調用所在的接受者類型的實例稱爲擴展接受者。
class Person(var name: String) 

class Student {
	var name=1
	//在Student類中定義Person的擴展方法,這個擴展方法在Student外不可用
    fun Person.showName() {
        println(name)//調用Person中的name字段
        println(this@Student.name)//調用Student中的name字段
    }

    fun diaoyong() {
    //調用擴展方法,這裏的Person("kaylee")爲擴展接受者
        Person("kaylee").showName()
    }

}

fun main(args: Array<String>) {
//這裏的Student()爲分發接受者
    Student().diaoyong()
}

對於分發接受者與擴展接收者的成員名字衝突的情況,擴展接受者優先。要引用分發接受者的成員可以使用this@分發接受者名.成員名字的方式調用
聲明爲成員的擴展可以聲明爲open並在子類中覆蓋。

class Person(var name: String)

open class Student {
    var name = 1
    open fun Person.showName() {
        println(name)
        println(this@Student.name)
    }

    fun diaoyong() {
        Person("kaylee").showName()
    }

}

class Primenor : Student() {
    //覆蓋基類的Person的擴展方法
    override fun Person.showName() {
        println(name+1)
        println(this@Primenor.name+1)
    }
}

fun main(args: Array<String>) {
    Primenor().diaoyong()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章