kotlin支持在不修改類代碼的狀況下,動態爲類添加屬性(擴展屬性)和方法(擴展方法)。java
擴展方法執行靜態解析(編譯時),成員方法執行動態解析(運行時)。函數
定義一個函數,在被定義的函數前面添加「類名.」,該函數即爲該類名對應類的拓展方法。工具
fun main(args: Array<String>) { val extensionClass = ExtensionClass() //調用拓展方法 extensionClass.test() } //定義一個空類 class ExtensionClass //爲該空類定義一個拓展方法test()方法 fun ExtensionClass.test() = println("我是ExtensionClass的拓展方法")
若是被擴展的類的擴展方法與該類的成員方法名字和參數同樣,該類對象調用該方法時,調用的會是成員方法。this
fun main(args: Array<String>) { val extension = ExtensionTest() //此處調用的會是成員方法 extension.test() } class ExtensionTest { fun test() = print("成員方法") } //該方法不會被調用 fun ExtensionTest.test() = println("擴展方法")
fun main(args: Array<String>) { val str = "123456" //調用String的拓展方法 println(str.lastIndex()) } //爲String定義一個拓展方法 fun String.lastIndex() = length - 1
java是一門靜態語言,沒法動態的爲類添加方法、屬性,除非修改類的源碼,並從新編譯該類。日誌
kotlin擴展屬性、方法時看起來是爲該類動態添加了成員,實際上並無真正修改這個被擴展的類,kotlin實質是定義了一個函數,當被擴展的類的對象調用擴展方法時,kotlin會執行靜態解析,將調用擴展函數靜態解析爲函數調用。code
靜態解析:根據調用對象、方法名找到拓展函數,轉換爲函數調用。對象
如(2)str.lastIndex()方法執行的過程爲:
①檢查str類型(發現爲String類型);繼承
②檢查String是否認義了lastIndex()成員方法,若是定義了,編譯直接經過;字符串
③若是String沒定義lastIndex()方法,kotlin開始查找程序是否有爲String類擴展了lastIndex()方法(便是否有fun String.lastIndex()),若是有定義該擴展方法,會執行該擴展方法;get
④既沒定義lastIndex()成員方法也沒定義擴展方法,編譯天然不經過。
因爲靜態調用擴展方法是在編譯時執行,所以,若是父類和子類都擴展了同名的一個擴展方法,引用類型均爲父類的狀況下,會調用父類的擴展方法。
/** * 拓展屬性、方法 */ 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("子類擴展方法")
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)
kotlin容許動態爲類擴展屬性,擴展屬性是經過添加get、set方法實現,沒有幕後字段(filed)。
擴展屬性也沒有真的爲該類添加了屬性,只能說是爲該類經過get、set方法計算出屬性。
限制:①擴展屬性不能有初始值;②擴展屬性不能用filed關鍵字訪問幕後字段;③val必須提供get方法,var必須提供get和set方法。
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"
在某個類裏面爲其餘類定義擴展方法、屬性,該擴展的方法,只能在該類中經過被擴展的類的對象調用擴展方法。
以類成員方式定義的擴展,屬於被擴展的類,所以在擴展方法直接調用被擴展的類的成員(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() } }
擴展方法(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 "我是來自帶接收者的匿名擴展函數的返回值" }
與普通函數同樣,匿名擴展方法也有函數類型,(1)中的函數類型爲:ExtensionTest.(String) -> String
若是能根據上下文推斷出接收者類型,則可使用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("匿名擴展函數傳入形參")}") }
擴展極大的增長了程序的靈活性,java若是想對一個類擴展某些屬性,必須經過繼承的方式等實現,kotlin使用語法直接能夠動態的擴展,能更方便組織一些工具方法等。
fun main(args: Array<String>) { "打印日誌".log() } /** * 爲String字符串添加一個打印日誌的擴展方法 */ fun String.log() { println(this) }