前言
爲了更動態的解決函數的擴展問題,進而提升編碼效率。這算是一項程序員的福利。
除了文字版本,也有Xmind版本 github地址
目錄結構是這樣的
- 定義
- 擴展函數
- 擴展函數是靜態解析的
- 可空接收者
- 擴展屬性
- 伴生對象的擴展
- 擴展的作用域
- 擴展聲明爲成員
定義
Kotlin 能夠擴展一個類的新功能而無需繼承該類或者使用像裝飾者這樣的設計模式。
比如說
- 爲第三方庫中無法修改的類編寫 新function,新function就如同該類原來就存在的函數一般使用。
- 也可以爲第三方類擴展一些新的屬性
擴展函數
可以在已有類中添加新的方法,不會對原類做修改
fun receiverType.functionName(params){
body
}
receiverType
表示函數的接收者,也就是函數擴展的對象
functionName
擴展函數的名稱
params
擴展函數的參數,可以爲NULL
範例
class User(var name:String)
/**擴展函數**/
fun User.Print(){
print("用戶名 $name")
}
fun main(arg:Array<String>){
var user = User("Runoob")
user.Print()
}
是不是有一種js的既視感
擴展函數是靜態解析的
具體被調用的的是哪一個函數
由調用函數的的對象表達式來決定
open class C
class D: C()
fun C.foo() = "c" // 擴展函數 foo
fun D.foo() = "d" // 擴展函數 foo
fun printFoo(c: C) {
println(c.foo()) // 類型是 C 類
}
fun main(arg:Array<String>){
printFoo(D()) //#輸出c
}
若擴展函數和成員函數一致
優先使用成員函數
class C {
fun foo() { println("成員函數") }
}
fun C.foo() { println("擴展函數") }
fun main(arg:Array<String>){
var c = C()
c.foo() //#輸出 成員函數
}
可空接收者
範例
fun Any?.toMyString(): String {
if (this == null) return "null"
// 空檢測之後,“this”會自動轉換爲非空類型,所以下面的 toString()
// 解析爲 Any 類的成員函數
return toString()
}
這裏的Any?是一個可以爲null的對象;
通過 this == null的判斷,可以讓你在沒有進行null判斷,就調用toMyString方法
擴展屬性
val <T> List<T>.lastIndex: Int
get() = size - 1
lastIndex是擴展出來的屬性
只能通過get來獲取
擴展屬性只能被聲明爲 val
允許定義在類或者kotlin文件中
不允許定義在函數中
否則會報Local extension properties are not allowed
範例
class Outer2 { // 外部類
private val bar: Int = 1
}
val Outer2.newBar : Int
get() = 222212
fun main(args: Array<String>) {
println(Outer2().newBar) // 222212
}
因爲是擴展的屬性,他是不具備初始化器的。
- 擴展不能真正的修改他們所擴展的類
- 你並沒有在一個類中插入新成員
- 僅僅是可以通過該類型的變量用點表達式去調用這個新函數
範例二
val House.number = 1 // 錯誤:擴展屬性不能有初始化器
伴生對象的擴展
class MyClass {
companion object { } // 將被稱爲 "Companion"
}
companion object即伴生對象
伴生對象在類中只能存在一個
類似於java中的靜態方法
這可以這麼寫。
範例一
class MyClass {
companion object CcName{
} // 將被稱爲 "Companion"
}
fun MyClass.CcName.printCompanion() { println("companion") }
fun main() {
MyClass.CcName.printCompanion()
}
範例二
class MyClass {
companion object { } // 將被稱爲 "Companion"
}
fun MyClass.Companion.printCompanion() { println("companion") }
fun main() {
MyClass.printCompanion()
}
擴展的作用域
與java一樣
需要import具體的,或者*
大多數時候我們在頂層定義擴展
- 直接在包裏
- 擴展範例
package org.example.declarations fun List<String>.getLongestString() { /*……*/}
使用所定義包之外的一個擴展
調用範例
package org.example.usage
import org.example.declarations.getLongestString
fun main() {
val list = listOf("red", "green", "blue")
list.getLongestString()
}
擴展聲明爲成員
在一個類內部你可以爲另一個類聲明擴展
範例
class Host(val hostname: String) {
fun printHostname() { print(hostname) }
}
class Connection(val host: Host, val port: Int) {
fun printPort() { print(port) }
fun Host.printConnectionString(p: Int) { //這是擴展函數。函數內的this指向Host本身。
//但是可以通過this指定到Connection的this
printHostname() // calls Host.printHostname()
print(":")
printPort() // calls Connection.printPort()
}
fun connect() {
/*……*/
host.printConnectionString(port) // calls the extension function
}
}
fun main() {
Connection(Host("kotl.in"), 443).connect()
//Host("kotl.in").printConnectionString(443) // error, the extension function is unavailable outside Connection
}
這個範例,kotlincn的說法其實是有點繞口的。細細品味,文中所謂的,不正說的是 this的指向問題嗎?