在 Kotlin 中可以將類型與類型建構式分別指定給變數, 到底有何不同, 看以下例子:
class AAA { // 類型 AAA, 預設的建構式就是 AAA( )
var b = 3 // 這是待化身的變數宣告, 透過建構式 AAA( ) 化身成物件成員(instance member)
companion object {// 這是協同物件, 也就是 AAA 的變身, 唯一實體共享物件(static object)
var a = 5 // 需透過 AAA 存取成員
var b = 10 // 名字同化身的成員, 直接變身成物件成員, 必需透過 AAA 存取成員
}
}
fun main(args: Array < String > ) {
val c = AAA // c 是 AAA 的變身(companion object), 但不是化身(instance of class)
val i = AAA( ) // i 才是類型的化身, 透過類型建構式 ( ) 才能化身成物件(instance of class)
println(c.a) // a 是 AAA 共享成員
println(c.b) // b 也是 AAA 成員之一
println(i@AAA.a) // i 要透過變身標籤 @AAA 才能存取共享成員 a
println(i@AAA.b) // i 要透過變身標籤 @AAA 才能存取共享成員 b
// println(i.a) // 錯誤的用法: a 實際並非 i 的成員
// println(i.b) // 錯誤的用法: b 實際並非 i 的成員
println(i.b) // 這裡的 b 是透過建構式生成的, 並非變身(companion object)裡面的 b
執行結果:
5
10
5
10
3
變身與化身中文意義是相同的, 如果把英文的 companion object(協同物件)翻譯成"變身", 而將 instance of class (類型物件)翻譯成"化身"時, 兩者本質上都是物件, 就要區分它們的不同, 也許可以這樣理解: 一旦宣告了協同物件(簡稱變身), 類型名稱可以變身(ailias of companion object)成物件去存取共享成員, 而類型物件(instance of class)必需透過建構式去化身, 兩者物件的生成方式不同在於: 變身在是在編譯階段(compile phase)生成的靜態物件(static object),從程式開始執行到結束生命都一直維係著不會消失, 但化身是在執行階段(runtime)才生成的區域物件(scope object), 生命周期受到控制, 一旦離開所在的生命區域(scope)便消失殆盡. 變身主要目的是維繫共享資源, 實際上只有一份變身物件存在類型內, 所有類型的化身並沒有變身這個物件!如果真要在化身物件方便取用,除了用 @標籤外, 也可以用 set() 及 get() 刻意連結起來,只不過要注意的是, 因為是共享的(static object),若在多工環境下也許要用 semaphore 來加以鎖定.
class AAA { // 類型 AAA, 預設化身的建構式就是 AAA( )
companion object {// 這是協同物件, 也就是 AAA 的變身, 唯一實體共享物件(static object)
var a = 5 // AAA 變身的成員
var b = 10 // 若名稱與化身的成員相同時, 必需透過 AAA 存取成員
}
var b: Int get( ) { return AAA.b } // 符號讀取連結, 讓化身間接讀取變身成員 b
set(v: Int) { AAA.b = v } // 符號寫入連結, 讓化身間接寫入變身成員 b
fun a( ) { // 當化身成員名稱與變身的成員不同時, 化身可以取用到變身的成員只是因為內定(default)符號聯結的關係, 並不表示他就是化身的成員
return a // 等同取用 AAA.a , 只是因為 kotlin 內定會將符號 a 聯結到變身成員 AAA.a
}
}
fun main(args: Array < String > ) {
val c = AAA // c 是 AAA 的變身(companion object), 但不是化身(instance of class)
val i = AAA( ) // i 是類型的化身, 透過類型建構式 ( ) 化身成物件(instance of class)
println(c.a) // a 是 AAA 共享成員
println(c.b) // b 也是 AAA 成員之一
println(i.a()) // 呼叫函式 a( )間接讀取 AAA.a
println(i.b) // 已經刻意將 b 做了符號讀取連結, 看似直接(實際是間接)讀取
i.b = 3 // 已經刻意將 b 做了符號寫入連結, 看似直接(實際是間接)寫入
println(c.b) // 因為 b 是共享成員, 所以變身的成員也跟著改變
}
結果:
5
10
5
10
3
沒有留言:
張貼留言