我的Kotlin學習之旅(六)
擴充套件
初次見到擴充套件的概念的時候,確實是有點不太理解的,完全搞不懂這是個什麼東東。
翻譯文件上是這麼說的:
與 C# 和 Gosu 類似, Kotlin 也提供了一種,可以在不繼承父類,也不使用類似裝飾器這樣的設計模式的情況下對指定類進行擴充套件。我們可以通過一種叫做擴充套件的特殊宣告來實現他。Kotlin 支援函式擴充套件和屬性擴充套件。
好吧,沒怎麼看懂,就只能通過具體的例子來理解它了。
這裡試一下函式擴充套件,為了宣告一個函式擴充套件,我們需要在函式名前面新增一個接受者型別來作為字首(意思是這種型別的物件可以呼叫該擴充套件函式),接下來是具體的例子:
fun ArrayList<Int>.swap(x: Int,y :Int){
val temp = this[y]
this[y] = this[x]
this[x] = temp
}
我在這裡定義了一個swap函式,作用是交換型別是ArrayList的物件的指定的兩個位置的值,同時這個ArrayList物件的值得型別是Int(這裡只是為了舉例子方法,其實也可以定義成泛型T)。
然後看一下該函式的使用:
class MainActivity : AppCompatActivity() {
private var mShow: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mShow = findViewById(R.id.show) as TextView?
val list: ArrayList<Int> = ArrayList()
list.add(111)
list.add(222)
list.add(333)
list.swap(0,1)
var str = ""
for (i in 0 until list.size) {
str += (""+list[i] + ";")
}
mShow!!.text = str
}
}
我在這裡聲明瞭一個型別為ArrayList的list,它的長度是3,0,1,2三個位置上的值分別是 111,222,333
呼叫list.swap(0,1)交換了0和1位置上的數值,應該是變成 222,111,333
通過xml中的TextView,將資料展示出來,最後顯示的結果就是222,111,333
說明這樣做是可行的。
至此,總結一下函式擴充套件:定義了類似於上述的擴充套件函式之後,函式的型別接受者就可以直接通過例項來呼叫該函數了。
最後再看一下文件,深入瞭解一下擴充套件的概念:
擴充套件實際上並沒有修改它所擴充套件的類。定義一個擴充套件,你並沒有在類中插入一個新的成員,只是讓這個類的例項物件能夠通過.呼叫新的函式。
再來看一個很重要的概念:擴充套件是被靜態解析的
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
用文件裡的程式碼來解釋吧,這裡有一個可被繼承的類C,D繼承自C,然後類C有一個擴充套件函式foo(),它列印的值是“c”,而它的繼承者D,也有一個相同名字的擴充套件函式,列印的值是”d”。最後有一個printFoo(c: C),重點在這裡,
它這裡接收一個形參,C。由於D繼承自C,所以也可以被接收。
那麼最後呼叫printFoo函式,輸入D物件,輸出的到底是”c”還是”d”呢?
這就用到了這句話:擴充套件是被靜態解析的。這裡的擴充套件函式的呼叫的決定這在於printFoo函式的那個形參的型別,也就是這裡的決定者是C,而不是實際傳入的引數。所以只要記住這句話:擴充套件函式的呼叫決定於是宣告的型別,而不是實際傳入的型別
再來看一個優先順序問題:如果一個類中,有一個和該類的擴充套件函式同名同參數的成員函式,那麼當該類的物件在呼叫該名稱的方法時,會呼叫哪個呢?
答案:如果有同名同參數的成員函式和擴充套件函式,呼叫的時候必然會使用成員函式
也就是說,成員函式的優先順序要比擴充套件函式的優先順序要高
可空的接收者
擴充套件可以使用空接收者型別進行定義。這樣的擴充套件使得,即使是一個空物件仍然可以呼叫該擴充套件,然後在擴充套件的內部進行 this == null 的判斷。這樣你就可以在 Kotlin 中任意呼叫 toString() 方法而不進行空指標檢查:空指標檢查延後到擴充套件函式中完成
fun Any?.toStrings(): String {
if (this == null) return "null"
// 在空檢查之後,`this` 被自動轉為非空型別,因此 toString() 可以被解析到任何類的成員函式中
return toString()
}
屬性擴充套件
直接看例子:
val <T>ArrayList<T>.lastIndex: Int
get() = size -1
使得型別是ArrayList的物件的名為lastIndex的擴充套件屬性的值為 list的長度 - 1,即返回的是list的最後一個值得下標
親測有用喲
需要注意的點:
擴充套件並不會真正給類添加了成員屬性
擴充套件屬性只能夠通過明確提供 getter 和 setter方法來進行定義.