[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

https://ithelp.ithome.com.tw/upload/images/20201004/201299028eH3RKaHWK.png (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 的原始碼

https://ithelp.ithome.com.tw/upload/images/20201004/20129902fivADYBNId.png

在自訂 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 ,所以擴充屬性不能有初始值

https://ithelp.ithome.com.tw/upload/images/20201004/20129902kVhdHpscSt.png

擴充伴生物件(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 的方法!

https://ithelp.ithome.com.tw/upload/images/20201004/20129902g9kgs5zVtD.png

以上就是今天的內容!謝謝大家!

今日練習的程式在這: 請點我