【Kotlin】擴充套件屬性、擴充套件函式

little_fat_sheep發表於2024-04-08

1 類的擴充套件

​ Kotlin 提供了擴充套件類或介面的操作,而無需透過類繼承或使用裝飾器等設計模式,來為某個類新增一些額外的屬性或函式,我們只需要透過一個被稱為擴充套件的特殊宣告來完成。透過這種機制,我們可以將那些第三方類不具備的功能強行進行擴充套件,方便我們的操作。

1.1 擴充套件內建類

1.1.1 擴充套件屬性

fun main() {
    var str = "abc"
    str.property = 5 // 列印: set property, value=5
    var property = str.property // 列印: get property
    println(property) // 列印: 10
}

var String.property: Int
    set(value) {
        println("set property, value=$value")
    }
    get() {
        println("get property")
        return 10
    }

​ 說明:擴充套件屬性並不是真的往類中新增屬性,也不會真的插入一個成員欄位到類的定義中,導致沒有變數儲存我們的資料,我們只能明確定義一個 getter 和 setter 來建立擴充套件屬性,才能讓它使用起來真的像是類的屬性一樣。

​ 如下,如果沒有定義 getter 和 setter方法,將會報錯如下。

img

1.1.2 擴充套件函式

fun main() {
    var str = "abc"
    str.myFun("xxx") // 列印: myFun, this=abc, value=xxx
}

fun String.myFun(value: String) {
    println("myFun, this=$this, value=$value")
}

1.2 擴充套件自定義類

1.2.1 擴充套件屬性

1)擴充套件新屬性

fun main() {
    var stu = Student()
    stu.name = "Mary" // 列印: set name, value=Mary
    var name = stu.name // 列印: get name
    println(name) // 列印: Tom
}

class Student

var Student.name: String
    set(value) {
        println("set name, value=$value")
    }
    get() {
        println("get name")
        return "Tom"
    }

2)擴充套件舊屬性

fun main() {
    var stu = Student("Jack")
    stu.name = "Mary" // 無列印
    var name = stu.name // 無列印
    println(name) // 列印: Mary
}

class Student(var name: String)

var Student.name: String // 擴充套件屬性不會生效
    set(value) {
        println("set name, value=$value")
    }
    get() {
        println("get name")
        return "Tom"
    }

​ 說明:如果擴充套件類中的同名同型別屬性,擴充套件將不會生效。

1.2.2 擴充套件函式

1)擴充套件新函式

fun main() {
    var stu = Student("Mary")
    stu.test() // 列印: test, name=Mary
}

class Student(var name: String)

fun Student.test() {
    println("test, name=$name")
}

2)擴充套件舊函式

fun main() {
    var stu = Student("Mary")
    stu.test() // 列印: inner, name=Mary
}

class Student(var name: String) {
    fun test() {
        println("inner, name=$name")
    }
}

fun Student.test() { // 擴充套件函式不會生效
    println("extended, name=$name")
}

​ 說明:如果擴充套件類中的同名同參函式,擴充套件將不會生效。

1.3 重寫父類的擴充套件函式

​ 類的擴充套件是靜態的,實際上並不會修改它們原本的類,也不會將新成員插入到類中,僅僅是將我們定義的功能變得可呼叫,使用起來就像真的有一樣。同時,在編譯時也會明確具體呼叫的擴充套件函式。

1.3.1 擴充套件屬性

fun main() {
    var stu: Student = Student()
    var name1 = stu.name // 列印: Student, get name
    println(name1) // 列印: Mary
    var peo: People = stu
    var name2 = peo.name // 列印: People, get name
    println(name2) // 列印: Tom
}

open class People
class Student: People()

var People.name: String
    set(value) {
        println("People, set name, value=$value")
    }
    get() {
        println("People, get name")
        return "Tom"
    }

var Student.name: String
    set(value) {
        println("Student, set name, value=$value")
    }
    get() {
        println("Student, get name")
        return "Mary"
    }

1.3.2 擴充套件函式

fun main() {
    var stu: Student = Student()
    println(stu.type()) // 列印: Student
    var peo: People = stu
    println(peo.type()) // 列印: People
}

open class People
class Student: People()

fun People.type() = "People"
fun Student.type() = "Student"

1.4 匿名擴充套件函式

​ 匿名函式的使用詳見 →【Kotlin】函式

fun main() {
    var myFun: String.() -> Int = fun String.(): Int {
        return this.length
    }
    println("abc".myFun()) // 列印: 3
}

​ 可以使用 Lambda 表示式簡化如下,Lambda 表示式的使用詳見 → 【Kotlin】Lambda表示式

fun main() {
    var myFun: String.() -> Int = {
        this.length
    }
    println("abc".myFun()) // 列印: 3
}

1.5 擴充套件函式作為引數

fun main() {
    var len = "abc".len { this.length }
    println(len) // 列印: 3
}

fun String.len(func: String.() -> Int): Int {
    return func()
}

2 官方擴充套件函式

2.1 原始碼

​ Kotlin 提供了一些泛型擴充套件函式(在 Standard.kt 中),如:apply、also、run、let、takeIf、takeUnless 等。

​ apply、also、run、let 的區別如下,作用是執行 block 函式的內容,並且返回 T 或 block 的返回值,通常用於判空處理。

函式 block是否為擴充套件函式 block入參 block返回值 函式返回值 訪問 T 物件
apply T this
also T T it
run R R this
let T R R it

2.1.1 apply

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

2.1.2 also

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

2.1.3 run

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

2.1.4 let

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

2.1.5 takeIf

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

2.1.6 takeUnless

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

2.1.7 with

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

​ 說明:with 不是擴充套件函式,但入參 block 是擴充套件函式。

2.1.8 to

public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

2.1.9 toList

public fun <T> Pair<T, T>.toList(): List<T> = listOf(first, second)
public fun <T> Triple<T, T, T>.toList(): List<T> = listOf(first, second, third)

2.2 應用

2.2.1 apply 應用

fun main() {
    var stu = Student()
    test(stu)
}

fun test(stu: Student?) {
    stu?.apply {
        this.name = "Tom"
        this.age = 23
    }
    println(stu) // 列印: [Tom, 23]
}

class Student {
    var name: String? = null
    var age: Int = 0

    override fun toString(): String {
        return "[$name, $age]"
    }
}

​ 說明:also、run、let 的應用與 apply 類似。

2.2.2 takeIf 應用

fun main() {
    var str = "ABC"
    var str2 = str.takeIf { it.length % 2 == 1 }?.let { it + it.reversed().substring(1) }
    println(str2) // 列印: ABCBA
}

2.2.3 with 應用

fun main() {
    var str = "ABC"
    var len = with(str) { this.length }
    println(len) // 列印: 3
}

​ 宣告:本文轉自【Kotlin】擴充套件屬性、擴充套件函式

相關文章