iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 28
0
Software Development

新手也能懂的 Kotlin Collection 賞玩門道系列 第 28

第二十八天:深入 Collection 核心 - Extension

上一個章節我們在探索 forEach() 原始碼的時候,除了使用到泛型、Lambda、inline 等技巧外,其實還有用到 Kotlin 的 Extension 語法。Extension 是 Kotlin 裡一個很重要的特點,Kotlin 的標準函式庫裡也大量的使用這個技巧,甚至 Collection 裡的許多實作也是用 Extension 做出來的。在這個章節裡,我們就要來討論 Extension 的用法。

什麼是 Extension?

Extension 是指在不直接修改 Class 定義的情況下,增加 Class 的功能。當我們無法接觸某個 Class 定義,或者該 Class 沒有使用 open 修飾符導致無法繼承時,就是使用 Extension 的最好時機。比方說,我們希望 String 可以多一個 method,但實際上你太可能去修改 Kotlin 原始碼;若是想要用繼承來擴充,一追原始碼也會發現 String 並沒有用 open 修飾符。這時我們就可以用 Extension 來達成!

定義一個 Extension 很簡單,就像宣告一個函式一樣,只是要在函式名稱前面加上接收者類型(Receiver Type)。在下面這一段範例裡,你可以看到函式名稱 surprise() 前多了 String. 的宣告,意思是這個 Extension 是擴充 String 的 method,而 String 就是這個 Extension 的 Receiver。

// 宣告一個 String 的 Extension
fun String.surprise(amount: Int = 3): String {
    return this + "!".repeat(amount)
}

// 任何 String 都可以使用這個 method
println("Wow".surprise()) // Wow!!!

值得一提的是,Extension 也適用於繼承,也就是說,假如今天有某個類型繼承 String(假如可以的話),那繼承的 Class 身上也一樣會有 surprise() method 可以呼叫。

讓 Extension 支援泛型

Extension 也可以支援泛型,Collection 原始碼裡的 forEach() 就是一個例子:

public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

我們可以看到 forEach()Iterable<T> 的 Extension,Iterable 本身支援泛型,forEach() 的 Lambda 參數也支援泛型。像這樣的泛型 Extension 在 Kotlin 標準函式庫內很常見,比方說 let() 函式的原始碼是這樣:

public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

因為 let()T 的 Extension,所以能夠支援任何型別。相較於把 let() 做成 Any 的 Extension,做成泛型 Extension 不僅可以支援任何型別的 Receiver,同時還保留了 Receiver 的型別資訊,對編譯器來說更加明確。

標準函式庫裡的 Extension

看完這個章節後,假如對標準函式庫裡的 Extension 有興趣的話,不妨用 IntelliJ IDEA 搜尋 Strings.kt 這個 String 的原始檔,裡面應該就可以看到很多 String 類別的 Extension 宣告。標準函式庫裡的類別 Extension,通常是以類別名稱加 s 結尾來命名,像是 Sequences.ktRanges.ktMaps.kt…等。這些檔案提供的功能,都是在各自的類別上以 Extension 擴充的。

像這樣大量使用 Extension 定義核心 API 功能,可以讓標準函式庫既保持輕量,同時還能提供更多的功能。因為一個 Extension 可以適用於多個 Class,所以也能有效的節省空間。

以上 6 章試著從 Collection 原始碼裡深入探索其核心,希望能讓大家更了解 Collection 的底層奧妙。從下一章開始,我們要來討論幾個活用 Collection 的方法。

參考資料

  • Kotlin 權威 2.0:Android 專家養成術第 18 章 - 擴充

上一篇
第二十七天:深入 Collection 核心 - Lambda
下一篇
第二十九天:活用 Collection - Scope Function
系列文
新手也能懂的 Kotlin Collection 賞玩門道31

尚未有邦友留言

立即登入留言