前言
kotlin官網(wǎng) (中文版)和kotlin教程學(xué)習(xí)教程的筆記场躯。
一、擴(kuò)展函數(shù)
- 直接舉個(gè)例子吧
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this關(guān)鍵字指代接收者對象旅挤,這里指代list實(shí)例,
this[index1] = this[index2]
this[index2] = tmp
}
var list = mutableListOf(1, 2, 3)
list.swap(0, 2)//對任意一個(gè) MutableList<T> 對象調(diào)用這個(gè)擴(kuò)展函數(shù)
println(list.toString()) //輸出結(jié)果為[3, 2, 1]
-
擴(kuò)展函數(shù)是靜態(tài)解析的L吖亍!粘茄!
擴(kuò)展函數(shù)是靜態(tài)解析的G┪琛!柒瓣!
擴(kuò)展函數(shù)是靜態(tài)解析的H宕睢!芙贫!
重要的話說三遍搂鲫,意思如下:
調(diào)用擴(kuò)展函數(shù)時(shí), 具體被調(diào)用的函數(shù)是哪一個(gè), 是通過調(diào)用函數(shù)的對象表達(dá)式的類型來決定的, 而不是 在運(yùn)行時(shí)刻表達(dá)式動(dòng)態(tài)計(jì)算的最終結(jié)果類型決定的
不太清晰,請看下例:
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D()) //輸出c
那么磺平,如果類中存在成員函數(shù), 同時(shí)又在同一個(gè)類上定義了同名同參數(shù)的擴(kuò)展函數(shù)會怎樣魂仍?這種情況下總是會優(yōu)先使用成員函數(shù)
class C {
fun foo() { println("member") }
}
fun C.foo() { println("extension") }
c.foo() // 輸出member
- 將擴(kuò)展定義為成員,看下例:
class C
class D {
fun C.foo() {
println(toString())
println(this@D.toString())
}
fun call(c: C) {
c.foo()
}
}
D().call(C())
輸出結(jié)果如下:
這里,我們可以看到拣挪,默認(rèn)調(diào)用了擴(kuò)展方法定義的類的方法蓄诽。
引入一個(gè)概念:
- 在類的內(nèi)部, 你可以為另一個(gè)類定義擴(kuò)展. 在這類擴(kuò)展中, 存在多個(gè) 隱含接受者(implicit receiver) - 這些隱 含接收者的成員可以不使用限定符直接訪問.
- 擴(kuò)展方法的定義所在的類的實(shí)例, 稱為_派發(fā)接受者(dispatch receiver),
擴(kuò)展方法的目標(biāo)類型的實(shí)例, 稱為 _擴(kuò)展接受者(extension receiver) .- 當(dāng)派發(fā)接受者與擴(kuò)展接受者的成員名稱發(fā)生沖突時(shí), 擴(kuò)展接受者的成員將會被優(yōu)先使用.
這個(gè)例子中,C為擴(kuò)展接受者媒吗,D為派發(fā)接受者。
- 小試牛刀
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")
}
}
//-----看好題了么乙埃?-測試開始---
C().caller(D()) //輸出闸英?锯岖?
C1().caller(D()) //輸出?甫何?
C().caller(D1()) //輸出出吹??
答案揭曉:
C().caller(D()) // 打印結(jié)果為 "D.foo in C"
C1().caller(D()) // 打印結(jié)果為 "D.foo in C1" - 派發(fā)接受者的解析過程是虛擬的
C().caller(D1()) // 打印結(jié)果為 "D.foo in C" - 擴(kuò)展接受者的解析過程是靜態(tài)的
你答對了么辙喂?
二捶牢、擴(kuò)展屬性
val String.upper
get() = toUpperCase()
var s = "aBcDefg"
println(s.upper)//輸出ABCDEFG
擴(kuò)展屬性不應(yīng)該初始化,因?yàn)閿U(kuò)展屬性沒有后端域變量巍耗,在上一節(jié)中我們說“初始化給定的值將直接寫入后端域變量中”秋麸,因此不能夠初始化。
那么為何沒有后端域變量呢炬太?由于擴(kuò)展屬性實(shí)際上不會向類添加新的成員, 因此無法讓一個(gè)擴(kuò)展屬性擁有一個(gè) 后端域變量
三灸蟆、對同伴對象(Companion Object)的擴(kuò)展
什么是同伴對象?由于Kotlin 的類沒有靜態(tài)方法, 大多數(shù)情況下, 建議使用包級函數(shù)替代, 或者使用同伴對象,達(dá)到靜態(tài)方法效果
package com.example.demo
class User {
companion object { //同伴對象
fun foo(){ }
}
}
fun pfoo(){} //包級函數(shù)
User.foo() //這樣亲族,調(diào)用的時(shí)候就不需要使用對象調(diào)用了
pfoo()
對這個(gè)同伴對象定義擴(kuò)展函數(shù)和擴(kuò)展屬性:
class User {
companion object { //同伴對象
fun foo() {}
}
}
fun pfoo() {} //包級函數(shù)
fun User.Companion.cfoo() {} // 對這個(gè)同伴對象定義擴(kuò)展函數(shù)
val User.Companion.getData: String // 對這個(gè)同伴對象定義擴(kuò)展屬性
get() = toString()
User.foo()
pfoo()
User.cfoo()//與同伴對象的常規(guī)成員一樣, 可以只使用類名限定符來調(diào)用這些擴(kuò)展函數(shù)和擴(kuò)展屬性
println(User.getData) //輸出com.example.demo.User$Companion@9bdf2f3
四炒考、使用擴(kuò)展的動(dòng)機(jī)
擴(kuò)展函數(shù)數(shù)是指在一個(gè)類上增加一種新的行為,甚至我們沒有這個(gè)類代碼的訪問權(quán)限霎迫。這是一個(gè)在缺少有用函數(shù)的類上擴(kuò)展的方法斋枢。在Java中,通常會實(shí)現(xiàn)很多帶有 static方法的工具類知给。Kotlin中擴(kuò)展函數(shù)的一個(gè)優(yōu)勢是我們不需要在調(diào)用方法的時(shí)候 把整個(gè)對象當(dāng)作參數(shù)傳入
總而言之就是瓤帚,解決**Utils.的麻煩,解決靜態(tài)導(dǎo)入無法使用代碼補(bǔ)全功能的麻煩