擴充既有的程式碼!

認識擴充函數

擴充可以在不修改類別定義的情況下增加類別的功能,跟繼承有點類似。不過當類別無法繼承的時候,擴充就變成一個不錯的選擇。接下來我們就來看看要怎麼定義擴充函數

如果今天我們的程式碼需要使用MD5運算的功能我們可以寫出這樣的函數

@OptIn(ExperimentalStdlibApi::class)
fun md5(text: String): String {
    val messageDigest = MessageDigest.getInstance("MD5")
    val digest = messageDigest.digest(text.toByteArray())
    return digest.toHexString()
}

fun main() {
    println(md5("Hello Kotlin!"))
}

雖然這樣子寫沒什麼問題,不過如果我們想要把md5這個函數當成是字串裡面的函數的使用話,就需要使用擴充函數,使用擴充函數寫出的md5函數會變成這樣

@OptIn(ExperimentalStdlibApi::class)
fun String.md5(): String {
    val messageDigest = MessageDigest.getInstance("MD5")
    val digest = messageDigest.digest(this.toByteArray())
    return digest.toHexString()
}

fun main() {
    println("Hello Kotlin!".md5())
}

我們可以看到使用擴充定義的函數使用起來就跟定義在類別裡面的函數一樣,可以透過這種方式把跟該類別相關的功能加進去,增加使用上的方便性。在定義擴充函數的時候被擴充的類型我們叫他接收者類型。

父類別的擴充函數

如同繼承一樣,如果父類別為擴充函數的接收者類型,子類別也可以一同使用擴充:

fun Any.println() {
    println(this)
}

fun main() {
    "Hello Kotlin!".md5().println()
    (3.1415926).println()
}

泛型擴充函數

我們也可以使用泛型來定義擴充函數,這樣可以增加程式的可用性

fun <T> T.println(): T {
    println(this)
    return this
}

fun main() {
    "Hello Kotlin!"
        .println()
        .md5()
        .println()
}

從Kotlin的標準函數看擴充函數

Kotlin的標準函數有大量使用泛型定義的擴充函數,以前我們初次看到的時候可能還不是那麼了解。不過學習到這邊之後,我們已經有能力看原始碼去了解這些標準函數的功能,不需要再靠死背去了解他們的功能了。

public inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}

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

public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    return if (predicate(this)) this else null
}

擴充屬性

我們也可以定義擴充屬性並且實作getter與setter

val String.numVowels
    get() = count { "aeiou".contains(it) }
    
fun main() {
    println("Hello Kotlin!".numVowels)
}

不過擴充屬性並沒有背後屬性,所以沒辦法初始化也沒有辦法儲存值,這是在實作擴充屬性的時候需要注意的事情。

Nullable型態擴充

擴充函數也可以適用在nullable型態上,這樣我們就能在擴充函數裡面處理一些nullable相關的問題

infix fun String?.getOrDefault(default: String): String = this ?: default

fun main() {
    val x = "123"
    val y: String? = null
    println(x getOrDefault "a")
    println(y getOrDefault "a")
}

藉由範例我們可以看到nullable的擴充函數由nullable型態或非nullable型態都可以呼叫。

infix關鍵字

infix關鍵字可以使用在類別函數或是擴充函數上,不過必須只有單個參數的函數才可以使用。呼叫時可以省略“.”與“()”,可以讓我們用比較簡潔的方式呼叫函數。

Last updated