分類
類中的屬性有兩種:
可以存儲值的屬性
-
不存儲屬性值答倡,每一次訪問都需要通過 getter / setter 方法底扳,因此每一次訪問都需要計算聊浅。
interface Parent { val name: String val age: Int get() { println("getter") return 10 } } class Child:Parent{ override val name = "name" // 可以存儲值催训。也可以通過 getter / setter 獲取修改 set(value) { println("setter ${field} ${value}") field = value } }
getter 與 setter
-
在方法內(nèi)部见咒,使用特殊標(biāo)識符 field 訪問支持字段的值偿衰。
class Child { var i = 0 var name = "name" set(value) { println("setter ${field} ${value}") field = value // 為字段賦值 } get() { println("get ${field}") i++ field = "${i}" return "${i}" } } fun main(args: Array<String>) { val c = Child() println(c.name) c.name = "main--" println(c.name) }
其輸出結(jié)果為:
-
可以修改 getter/setter 的可見性。
class Test { var name = "" private set(value) { // 將 setter 可見性設(shè)置為 private field = value } }
可見性
默認的修飾符是 public
kt 新增修飾符 internal:模板可見改览。一個模板指的是一起編譯的 kt 文件下翎。
protected 只能用于修飾類中的成員
可見性低的類不能用于可見性高的類、方法宝当、屬性视事。
與 java 一樣,重寫可以提升可見性庆揩,但不能降低可見性俐东。
internal class User()
open class Person()
class Student:Person(){
internal var user:User = User()
internal fun test():User = User()
}
上述代碼中,由于 User 定義為 internal盾鳞,所以 Student 類的 user 屬性犬性、test() 方法都只能定義成比 internal 或比它低的可見性瞻离。
同理腾仅,如果將 Person 類的可見性定義 internal ,則 Student 不能繼承套利,除非將 Student 也定義成 internal 或比它低的可見性推励。
修飾符 | 類成員 | 頂層聲明 |
---|---|---|
public(默認) | 所有地方可見 | 所有地方可見 |
internal | 模塊中可見 | 模塊中可見 |
protected | 子類可見 | - (不能修飾) |
private | 類中可見 | 文件中可見 |
屬性委托
使用關(guān)鍵字 by ,將屬性的訪問器委托給另一個實例肉迫。
屬性委托只有在屬性被訪問時才進行初始化验辞,與 lazy 函數(shù)類似。
格式
屬性委托基本語法如下:
class Demo{
var p:Type by Delegate()
}
屬性 p 將自己的 getter/setter 邏輯委托給了另一個對象喊衫,這里是 Delegate 的一個實例:通過對 by 后的表達式求值來獲取這個實例跌造。上述代碼等價如下代碼:
class Demo {
private val delegate = Delegate()
var p: Type
get() = delegate.getValue()
set(value) = delegate.setValue(value)
}
惰性初始化
lazy 函數(shù)會延遲加載,再結(jié)構(gòu) by 關(guān)鍵字可以實現(xiàn)委托屬性。
在代碼中壳贪,經(jīng)常需要使用 惰性初始化:只有當(dāng)需要該屬性的時候才對屬性進行初始化陵珍。
如下類,初始化時违施,并沒有初始化 s互纯,只有當(dāng)訪問 s 時才進行初始化,并且將初始化的值存儲于 _s 中磕蒲,這樣下次訪問時可以直接返回留潦,這就是所謂的惰性初始化:
class Test {
private var _s: String? = null
val s: String
get() {
if (_s == null)
_s = "sss"
return _s!!
}
}
上述代碼有點啰嗦,如果有多個惰性屬性辣往,這些模板代碼需要寫多次兔院。而 屬性委托可以讓代碼變得簡單,可以封裝用于存儲值的支持屬性以及確保該值只會被初始化一次排吴。
委托類的構(gòu)造
委托類有如下要求:
定義 getValue 與 setValue(如果是 val 變量秆乳,不需要)方法,且被聲明為 operator
方法至少有兩個參數(shù)钻哩,一個用于接收屬性的實例屹堰;一個用于表示屬性本身,其類型為 KProperty街氢〕都可以通過 KProperty.name 獲取屬性名。
結(jié)合關(guān)鍵字 by 珊肃,kt 編譯器自動完成 ‘惰性初始化‘ 中代碼的功能
fun main(args: Array<String>) {
val d = Demo()
println(d.a) // 會輸出 getValue 荣刑,然后再輸出 1
d.a = 3 // 會輸出 setValue
println(d.a) // 會輸出 getValue ,然后再輸出 3
}
class TestDelegate(var propValue: Int) {
operator fun getValue(demo: Demo, name: KProperty<*>): Int {
println("getValue")
return propValue
}
operator fun setValue(demo: Demo, name: KProperty<*>, newValue: Int) {
println("setValue")
propValue = newValue
}
}
class Demo {
var a: Int by TestDelegate(1)
}
從輸出中可以發(fā)現(xiàn):訪問 a 屬性時伦乔,所有的請求都轉(zhuǎn)發(fā)到 TestDelegate 對應(yīng)的方法中厉亏。這就是所謂的屬性委托。