[Day 25] Extension (擴充)
Extension (擴充)
Extension 是指在不修改原有 class
的狀況下,在已經建立好的 class 增加新的方法或屬性,可以針對 Kotlin 或 Java 既有的 class 做擴充,也可以對自己定義的 class 做擴充。
擴充函數
還記得以前在 Java 的時代,常常利用 Google guava 或 apache commons 等等的 static 方法的函式庫嗎?或者是自己定義了很多 xxxUtils 的 Java class。
在 Kotlin 因為有了 Extension ,讓我們能更順暢的定義自己的方法在既有的 class 之上!
先複習一下,像下面的例子, String 就是 receiver type
(from Kotlin in Action)
在既有 class 做擴充
像在下面的例子,我可以在 String 上直接新增一個新的函數,這個函數會做 trim() 和 toUpperCase(),這裡我還加上了 String?. 避免 nullable 的狀況,String? 就是 receiver type。
ps. 這個例子其實就是擴充 nullable class (Nullable receiver) 的例子。
fun String?.trimToUpperCase(): String {
return this?.trim()?.toUpperCase() ?: ""
}
結果如下, " tim has a ..." 就會是 receiver 物件
。
println(" tim has a ...".trimToUpperCase()) //TIM HAS A ...
val nullV = null
println(nullV.trimToUpperCase()) //空字串
當然,或許可以透過繼承來達到擴充的目的,但像是 String 因為原始碼中,並沒有 open 的關鍵字,所以其實就無法使用繼承來擴充了,此時 extension 就是最好的選擇了!
String 的原始碼
在自訂 class 做擴充
如下例, 定義了自己的 NumOp class 來做 BigDecimal 的乘法 fun NumOp.mutiply(num: BigDecimal)
,在這 NumOp 就是 receiver type
class NumOp(var num:BigDecimal)
fun NumOp.mutiply(num: BigDecimal) {
this.num *= num;
}
如下面的結果,num1 就會是 receiver 物件
val num1 = NumOp(BigDecimal("1000"))
num1.mutiply(BigDecimal("2000"))
println(num1.num) // 2000000
泛型擴充函數
當然,如果想要 receiver 是任何的型態,也可以使用泛型來做擴充。
fun <T> T.easyPrint(): T {
println(this)
return this
}
這樣就會會印出 receiver 物件的內容。
"test".easyPrint() // test
BigDecimal("2000").easyPrint() // 2000
擴充屬性
那如果想要在既有 class 下新增屬性的話呢,要透過 getter 和 setter 方法來擴充
val <T> List<T>.lastIndex: Int
get() = size - 1
而且擴充屬性也沒有 backing field ,所以擴充屬性不能有初始值。
擴充伴生物件(Companion object extensions)
也可以針對 companion object 做擴充,因為這裡 companion object 並沒有名稱,所以預設名稱會是 Companion,所以擴充時就會這樣做 fun MyClass.Companion.printCompanion()
class MyClass {
companion object { } // will be called "Companion"
}
fun MyClass.Companion.printCompanion() { println("companion") }
fun main() {
MyClass.printCompanion()
}
Kotlin 的擴充底層實作是 Java 的 static 方法
以第一個 trimToUpperCase() 的例子來看,查看 bytecode 就可以發現都是 static 的方法!
以上就是今天的內容!謝謝大家!
今日練習的程式在這: 請點我