kotlin擴展方法、屬性

1.概念

​ kotlin支持在不修改類代碼的狀況下,動態爲類添加屬性(擴展屬性)和方法(擴展方法)。java

2.擴展方法

​ 擴展方法執行靜態解析(編譯時),成員方法執行動態解析(運行時)。函數

(1)語法格式

​ 定義一個函數,在被定義的函數前面添加「類名.」,該函數即爲該類名對應類的拓展方法。工具

fun main(args: Array<String>) {
    val extensionClass = ExtensionClass()
    //調用拓展方法
    extensionClass.test()
}
//定義一個空類
class ExtensionClass
//爲該空類定義一個拓展方法test()方法
fun ExtensionClass.test() = println("我是ExtensionClass的拓展方法")

(2)成員方法優先

​ 若是被擴展的類的擴展方法與該類的成員方法名字和參數同樣,該類對象調用該方法時,調用的會是成員方法。this

fun main(args: Array<String>) {
    val extension = ExtensionTest()
    //此處調用的會是成員方法
    extension.test()
}

class ExtensionTest {
    fun test() = print("成員方法")
}
//該方法不會被調用
fun ExtensionTest.test() = println("擴展方法")

(3)爲系統類添加拓展方法(以String爲例)

fun main(args: Array<String>) {
    val str = "123456"
    //調用String的拓展方法
    println(str.lastIndex())
}
//爲String定義一個拓展方法
fun String.lastIndex() = length - 1

(4)擴展實現原理

​ java是一門靜態語言,沒法動態的爲類添加方法、屬性,除非修改類的源碼,並從新編譯該類。日誌

​ kotlin擴展屬性、方法時看起來是爲該類動態添加了成員,實際上並無真正修改這個被擴展的類,kotlin實質是定義了一個函數,當被擴展的類的對象調用擴展方法時,kotlin會執行靜態解析,將調用擴展函數靜態解析爲函數調用。code

靜態解析:根據調用對象、方法名找到拓展函數,轉換爲函數調用。對象

如(2)str.lastIndex()方法執行的過程爲:
​ ①檢查str類型(發現爲String類型);繼承

​ ②檢查String是否認義了lastIndex()成員方法,若是定義了,編譯直接經過;字符串

​ ③若是String沒定義lastIndex()方法,kotlin開始查找程序是否有爲String類擴展了lastIndex()方法(便是否有fun String.lastIndex()),若是有定義該擴展方法,會執行該擴展方法;get

​ ④既沒定義lastIndex()成員方法也沒定義擴展方法,編譯天然不經過。

(5)靜態解析調用擴展方法注意點

​ 因爲靜態調用擴展方法是在編譯時執行,所以,若是父類和子類都擴展了同名的一個擴展方法,引用類型均爲父類的狀況下,會調用父類的擴展方法。

/**
 * 拓展屬性、方法
 */
fun main(args: Array<String>) {
    val father : ExtensionTest = ExtensionTest()
    father.test()//調用父類擴展方法
    val child1 : ExtensionTest = ExtensionSubTest()
    child1.test()//引用類型爲父類類型,編譯時靜態調用的仍是父類的擴展方法
    val child2 : ExtensionSubTest = ExtensionSubTest()
    child2.test()//此時纔是調用子類的擴展方法
}

/**
 * 父類
 */
open class ExtensionTest

/**
 * 子類
 */
class ExtensionSubTest : ExtensionTest()

/**
 * 父類擴展一個test方法
 */
fun ExtensionTest.test() = println("父類擴展方法")

/**
 * 子類擴展一個test方法
 */
fun ExtensionSubTest.test() = println("子類擴展方法")

(6)可空類型擴展方法(以擴展equals方法爲例)

​ kotlin容許擴展可空類型擴展方法,這樣,null也能調用該方法。

fun main(args: Array<String>) {
    val a: Any? = null
    val b: Any? = null
    println(a.equals(b))
}

fun Any?.equals(any: Any?): Boolean = this != null && any != null && any.equals(this)

3.擴展屬性

(1)概念

​ kotlin容許動態爲類擴展屬性,擴展屬性是經過添加get、set方法實現,沒有幕後字段(filed)。

​ 擴展屬性也沒有真的爲該類添加了屬性,只能說是爲該類經過get、set方法計算出屬性。

​ 限制:①擴展屬性不能有初始值;②擴展屬性不能用filed關鍵字訪問幕後字段;③val必須提供get方法,var必須提供get和set方法。

(2)定義擴展屬性

fun main(args: Array<String>) {
    val extensionTest = ExtensionTest("a", "b")
    println(extensionTest.param1)//a
    println(extensionTest.param2)//b
    println(extensionTest.extensionParam)//a-b
}

/**
 * 定義一個類,包含屬性param一、屬性param2
 */
class ExtensionTest(var param1: String, var param2: String)

/**
 * 爲該類擴展屬性extensionParam
 */
var ExtensionTest.extensionParam: String
    set(value) {
        param1 = "param1$value"
        param1 = "param2$value"
    }
    get() = "$param1-$param2"

4.以類成員方式定義擴展

​ 在某個類裏面爲其餘類定義擴展方法、屬性,該擴展的方法,只能在該類中經過被擴展的類的對象調用擴展方法。

​ 以類成員方式定義的擴展,屬於被擴展的類,所以在擴展方法直接調用被擴展的類的成員(this能夠省略),同時由於它位於所在類中,所以又能夠直接調用所在類的成員。

fun main(args: Array<String>) {
    val extensionTest = ExtensionTest()
    val extensionTest2 = ExtensionTest2()
    extensionTest2.info(extensionTest)
}

/**
 * 定義一個類包含test方法
 */
class ExtensionTest {
    fun test() = println("ExtensionTest的test方法")
}

/**
 * 定義一個類包含test方法,包含ExtensionTest的一個擴展方法
 */
class ExtensionTest2 {
    val a = "a"
    fun test() = println("ExtensionTest2的test方法")
    fun ExtensionTest.func() {
        println(a)//調用擴展類的成員
        test()//調用被擴展類的成員,至關於this.test()
        this@ExtensionTest2.test()//同名的須要用this@類名的方式來調用
    }

    fun info(extensionTest: ExtensionTest) {
        extensionTest.func()
    }
}

5.帶接收者的匿名擴展函數

(1)概念

​ 擴展方法(fun 類名.方法名())去掉方法名就是所謂的帶接收者的匿名擴展函數,接收者就是類自己,形如:fun Int.() : Int。

fun main(args: Array<String>) {
    val extensionTest = ExtensionTest()
    println(extensionTest.noNameExtensionFun("向帶接收者的匿名函數傳入的參數"))//使用匿名擴展函數
}

/**
 * 定義一個空類
 */
class ExtensionTest

/**
 * 爲空類定義一個帶接收者的匿名擴展函數
 */
var noNameExtensionFun = fun ExtensionTest.(param: String): String {
    println(param)
    return "我是來自帶接收者的匿名擴展函數的返回值"
}

(2)帶接收者的匿名擴展函數的函數類型

​ 與普通函數同樣,匿名擴展方法也有函數類型,(1)中的函數類型爲:ExtensionTest.(String) -> String

(3)帶接收者的匿名擴展函數與lambda表達式

​ 若是能根據上下文推斷出接收者類型,則可使用lambda表達式

fun main(args: Array<String>) {
    test {
        println(it)
        "匿名擴展函數返回值"
    }
}

/**
 * 定義一個空類
 */
class ExtensionTest

/**
 * 定義一個函數,形參爲ExtensionTest.(String) -> String類型,至關於同時爲ExtensionTest類擴展了一個匿名擴展函數
 */
fun test(fn: ExtensionTest.(String) -> String) {
    val extensionTest = ExtensionTest()
    println("調用匿名擴展函數:${extensionTest.fn("匿名擴展函數傳入形參")}")
}

6.擴展使用場景

​ 擴展極大的增長了程序的靈活性,java若是想對一個類擴展某些屬性,必須經過繼承的方式等實現,kotlin使用語法直接能夠動態的擴展,能更方便組織一些工具方法等。

fun main(args: Array<String>) {
    "打印日誌".log()
}

/**
 * 爲String字符串添加一個打印日誌的擴展方法
 */
fun String.log() {
    println(this)
}
相關文章
相關標籤/搜索