kotlin類與對象——>擴展
1,擴展概念(OC中早期就有此功能)
Kotlin 能夠擴展一個類的新功能而無需繼承該類或者使用像裝飾者這樣的設計模式。這通過叫做 擴展 的特殊聲明完成。例如,你可以為一個你不能修改的、來自第三方庫中的類編寫一個新的函數。這個新 增的函數就像那個原始類本來就有的函數一樣,可以用普通的方法調用。這種機制稱為擴展函數此外,也有擴展屬性, 允許你為一個已經存在的類添加新的屬性
2.擴展函數,聲明一個擴展函數,我們需要用一個 接收者類型 也就是被擴展的類型來作為他的前綴。下面代碼為 MutableList<Int> 添加一個 swap 函數:
fun MutableList<Int>.swap(index1: Int, index2: Int) { val tmp = this[index1] // “this”對應該列表 this[index1] = this[index2] this[index2] = tmp } //這個 this 關鍵字在擴展函數內部對應到接收者對象(傳過來的在點符號前的對象)現在,我們對任意 MutableList<Int> 調用該函數了: val list = mutableListOf(1, 2, 3) list.swap(0, 2) // “swap()”內部的“this”會保存“list”的值 //當然,這個函數對任何 MutableList<T> 起作用,我們可以泛化它: //為了在接收者類型表達式中使用泛型,我們要在函數名前聲明泛型參數 fun <T> MutableList<T>.swap(index1: Int, index2: Int) { val tmp = this[index1] // “this”對應該列表 this[index1] = this[index2] this[index2] = tmp }
3.擴展是靜態解析的
擴展不能真正的修改他們所擴展的類。通過定義一個擴展,你并沒有在一個類中插入新成員,僅僅是可 以通過該類型的變量用點表達式去調用這個新函數。
我們想強調的是擴展函數是靜態分發的,即他們不是根據接收者類型的虛方法。這意味著調用的擴展函 數是由函數調用所在的表達式的類型來決定的,而不是由表達式運行時求值結果決定的。例如
open class Shape class Rectangle : Shape() fun Shape.getName() = "Shape" fun Rectangle.getName() = "Rectangle" fun printClassName(s: Shape) { println(s.getName()) } printClassName(Rectangle()) //這個例子會輸出 "Shape",因為調用的擴展函數只取決于參數 s 的聲明類型,該類型是 Shape 類 //如果一個類定義有一個成員函數與一個擴展函數,而這兩個函數又有相同的接收者類型、相同的名字,并且都適用給定的參數,這種情況總是取成員函數。例如: class Example { fun printFunctionType() { println("Class method") } } fun Example.printFunctionType() { println("Extension function") } Example().printFunctionType() //這段代碼輸出“Class method”。 //當然,擴展函數重載同樣名字但不同簽名成員函數也完全可以 class Example { fun printFunctionType() { println("Class method") } } fun Example.printFunctionType(i: Int) { println("Extension function") } Example().printFunctionType(1)
4.可空接收者
注意可以為可空的接收者類型定義擴展。這樣的擴展可以在對象變量上調用,即使其值為 null,并且可 以在函數體內檢測 this == null ,這能讓你在沒有檢測 null 的時候調用 Kotlin 中的toString():檢測發生在擴展函數的內部。
fun Any?.toString(): String { if (this == null) return "null" // 空檢測之后,“this”會自動轉換為非空類型,所以下面的 toString() // 解析為 Any 類的成員函數 return toString() }
5.擴展屬性,與函數類似,Kotlin 支持擴展屬性
val <T> List<T>.lastIndex: Int get() = size - 1
5.1 注意:由于擴展沒有實際的將成員插入類中,因此對擴展屬性來說幕后字段是無效的。這就是為什么擴 展屬性不能有初始化器。他們的行為只能由顯式提供的 getters/setters 定義
val House.number = 1 // 錯誤:擴展屬性不能有初始化器
6.伴生對象的擴展,如果一個類定義有一個伴生對象 ,你也可以為伴生對象定義擴展函數與屬性。就像伴生對象的常規成員 一樣,可以只使用類名作為限定符來調用伴生對象的擴展成員:
class MyClass { companion object {} // 將被稱為 "Companion" } fun MyClass.Companion.printCompanion() { println("companion") } fun main() { MyClass.printCompanion() }
7.擴展的作用域
//大多數時候我們在頂層定義擴展?直接在包里 package org.example.declarations fun List<String>.getLongestString() { /*......*/}
7.1 要使用所定義包之外的一個擴展,我們需要在調用方導入它
package org.example.usage import org.example.declarations.getLongestString fun main() { val list = listOf("red", "green", "blue") list.getLongestString () }
8.擴展聲明為成員
8.1 在一個類內部你可以為另一個類聲明擴展。在這樣的擴展內部,有多個 隱式接收者——其中的對象成員 可以無需通過限定符訪問。擴展聲明所在的類的實例稱為 分發接收者,擴展方法調用所在的接收者類型 的實例稱為 擴展接收者。
class Host(val hostname: String) { fun printHostname() { print(hostname) } } class Connection(val host: Host, val port: Int) { fun printPort() { print(port) } fun Host.printConnectionString() { printHostname() // 調用 Host.printHostname() print(":") printPort() // 調用 Connection.printPort() } fun connect() { /*......*/ host.printConnectionString() } } fun main() { Connection(Host("kotl.in"), 443).connect() //Host("kotl.in").printConnectionString(443) // 錯誤,該擴展函數在 Connection 外不可用 }
8.2 對于分發接收者與擴展接收者的成員名字沖突的情況,擴展接收者優先。要引用分發接收者的成員你可 以使用 限定的 this 語法。
class Connection { fun Host.getConnectionString() { toString() // 調用 Host.toString() this@Connection.toString() // 調用 Connection.toString() } } }
8.3 聲明為成員的擴展可以聲明為 open 并在子類中覆蓋。這意味著這些函數的分發對于分發接收者類型 是虛擬的,但對于擴展接收者類型是靜態的
open class Base {} class Derived : Base() {} open class BaseCaller { open fun Base.printFunctionInfo() { println("Base extension function in BaseCaller") } open fun Derived.printFunctionInfo() { println("Derived extension function in BaseCaller") } fun call(b: Base) { b.printFunctionInfo() // 調用擴展函數 } } class DerivedCaller : BaseCaller() { override fun Base.printFunctionInfo() { println("Base extension function in DerivedCaller") } override fun Derived.printFunctionInfo() { println("Derived extension function in DerivedCaller") } } fun main() { BaseCaller().call(Base()) // “Base extension function in BaseCaller” DerivedCaller().call(Base()) // “Base extension function in DerivedCaller”——分發接收者虛擬解析 DerivedCaller().call(Derived()) // “Base extension function in DerivedCaller”——擴展接收者靜態解析 }
9.關于可見行的說明
擴展的可?性與相同作用域內聲明的其他實體的可?性相同。例如: — 在文件頂層聲明的擴展可以訪問同一文件中的其他 private 頂層聲明; — 如果擴展是在其接收者類型外部聲明的,那么該擴展不能訪問接收者的 private 成員

浙公網安備 33010602011771號