Kotlin ---- 擴展屬性、擴展函數
在原有類的基礎上,增加功能,及不用改源碼,也不用寫子類!
簡單的示例
擴展屬性
val Float.dpTOpx
get()= TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,this,
Resources.getSystem().displayMetrics)
val Float.pxTOdp
get() = this/Resources.getSystem().displayMetrics.density+0.5f
println("轉換結果dpTOpx${200f.dpTOpx}")
println("轉換結果pxTOdp${200f.pxTOdp}")
高級用法 擴展函數
上面的擴展屬性,我們都比較容易上手,但是 擴展函數+函數引用混在一起,很多人就會搞蒙圈了
下面我們就來把它 講清楚:
作用域
其實寫在哪都可以(直接放在頂層目錄下作爲一個 Top-level Function),但是由於不應該給協同開發的同事,造成打擾,我們應該寫在
自己的工作package下 (com.zyg.myTest下)或者某個文件下。
給擴展函數 指定 一個Receiver (接受者)
Receiver 也就是說,指定誰能調用 這個函數。
歸屬問題
在一個類中去寫一個擴展函數
class ExampleTest {
fun String.method2(int:Int){
println(this+int)
}
...
"翻滾吧章魚".method2(2020) // print-> "翻滾吧章魚2020"
}
那麼他就是 ExampleTest 這個類的成員函數,只能在類的內部調用。
引用問題
lambda 中 函數可以通過 ‘’ :: " 雙冒號被指像的 (指像的並不是 函數本身,而是跟函數有相同功能的 ‘對象’ )。
那麼‘擴展函數同樣可以通過“ :: ”被指像 ’ (PS: 但這個方法 需要是 Top-level Function,類的成員函數則不行 )。
示例
fun String.method1(i:Int){
println("method1${i}")
}
...
(String::method1)("翻滾吧章魚",1) //kotlin 將第一個參數,Receiver調用者傳進去 ; 後面的參數則爲 方法的入參
ps: 可能有同學會問,爲什麼第一個參數要傳 調用者 Receiver 呢?
因爲 我們 拿到的是 函數引用的對象,而不是調用者的對象,我們不可能寫成 「 “翻過滾吧章魚”::method1」對吧。。。
那麼kotlin 提供的途徑就是,在這種調用情況下,增加一個函數參數,並且‘函數調用者’放在第一個參數
這樣我們就可以,用函數引用來調用,成員函數 和 擴展函數了。
引用賦值
可以複製給 兩種 不同的函數變量類型 :有 Receiver 和 無 Receiver 的
val a:String.(Int) -> String = String::method1 // 有 val b:(String,Int) -> String = String::method1 // 無
這兩種類型的 變量也可以相互轉換
val a:String.(Int) -> String = String::method1 val b:(String,Int) -> String = String::method1 val c:(String,Int) ->String = a val d:String.(Int) ->String = b
A. 等等,既然能相互轉換,是不是說明,無Receiver 的函數引用,也可以賦值給有Receiver 的變量麼?
我有一個大膽的想法:
fun method3(s:String,i:Int):String{
return s+i
}
...
val e:(String,Int) ->String = ::method3
val f:String.(Int) ->String = ::method3
竟然沒報錯!!!
那這樣的話,我們就可以將一個 普通的函數,使用Receiver 的方式來調用,這個可太爽了吧!
ps:使用的前提的 普通函數的第一個參數類型,要與Receiver 的類型相同。
"翻滾吧章魚".f(101) // 可以調用
"翻滾吧章魚".method3(101) // 這樣會報錯
B.同時反響操作也可以的,但是好像沒什麼用了(有Receiver 的函數引用,也可以賦值給無Receiver 的變量)
總結
本文涉及內容
- 擴展函數
- 擴展函數的引用
- 有無Reveiver 的函數類型的轉換以及 擴展屬性