屬性和域

屬性和域

什么是屬性?
實際是指getter和setter方法,雖然和字段是兩個概念,姑且可以理解為有g(shù)etter和setter方法的字段。
如何聲明?
完整語法:

var 屬性名: <屬性類型> [= <初始化器>]
    [<getter>]
    [<setter>]

其中的初始化器、getter和setter方法是可選的。如果沒有自定義getter和setter的實現(xiàn),就會使用默認的。
以下兩種情況屬性類型也可省略:

  1. 可以通過初始化器推斷出來
  2. 被聲明的屬性會覆蓋基類中的屬性,并且通過基類中屬性的類型推動出來

兩種屬性:
只讀屬性:val關(guān)鍵字聲明
可變屬性:var關(guān)鍵字聲明
訪問屬性:
訪問屬性使用點操作符,對象名.屬性名,如:

var person = Person()
person.name = "張三"

和oc一樣,點操作符內(nèi)部的實現(xiàn)也是調(diào)用getter或setter訪問器。并且在Kotlin中訪問Java的屬性時,仍然可以用點語法訪問Java對象中的屬性。

如果要修改訪問器的可見性或添加注解,但又不需要修改默認實現(xiàn),你可以重新定義方法,但不定義它的實現(xiàn),這樣仍然是使用默認實現(xiàn)。如:

var setterVisibility: String = "abc"
    private set // 設(shè)值方法的可見度為 private, 并使用默認實現(xiàn)

var setterWithAnnotation: Any? = null
    @Inject set // 對設(shè)值方法添加 Inject 注解

屬性的后端域變量(Backing Field)

Kotlin類不能擁有域變量(也就是Java中的成員變量),但是使用訪問器時又需要這種域變量,所以Kotlin提供了后端域變量,可以用field標識符來訪問。
如:

var counter = 0 // 初始化給定的值將直接寫入后端域變量中
    set(value) {
        if (value >= 0) field = value
    }
  }

field就代表編譯器自動生成的后端域變量,并且只允許在屬性訪問器中使用

什么情況下會生成后端域變量?

  1. 訪問器中任一個使用默認實現(xiàn)
  2. 自定義的訪問器中通過field關(guān)鍵字訪問屬性

下面這種情況下就不會生成后端域變量:

val isEmpty: Boolean
    get() = this.size() == 0 //自定義訪問器沒有使用field關(guān)鍵字訪問屬性

為什么不用this關(guān)鍵字訪問屬性呢?
試想下,this代表當前對象,而上面說過通過點操作符,訪問屬性時,實際上是調(diào)用了訪問器,在訪問器內(nèi)部實現(xiàn)中再調(diào)用訪問器就會出現(xiàn)無限遞歸。所以,通過Backing Filed可以避免這個問題。

后端屬性(Backing Property)

如果你的操作是想訪問屬性內(nèi)部的屬性,或者集合里面的元素,而不是屬性本身,那么你可以聲明這個屬性為private的(后端屬性),然后定義個public的屬性并自定義其訪問器,在自定義的訪問器內(nèi)部訪問后端屬性。如:

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        // 這里的操作不希望屬于 backing filed,
        if (_table == null) {
            _table = HashMap() 
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

其實也沒有什么神秘的,其實就像Java中我們通常寫的,在訪問私有屬性時,在自定義訪問器中添加些限制,避免取到或設(shè)置非法的值一個道理。只是在Kotlin中取了個高大上的名字。

編譯器常數(shù)值

值在編譯期就能確定的屬性,用const關(guān)鍵字修飾。滿足以下條件的屬性可以標記為編譯器常數(shù)值:

  1. 必須是頂級屬性或是object的成員
  2. 值必須是String或基本類型
  3. 不能自定義取值方法

如:

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

編譯器常數(shù)值可以用在注解內(nèi):

@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }

延遲初始化屬性

通常屬性被聲明為非null類型就必須就地初始化。但是,這種限制在很多情況下是不方便的,比如,聲明的屬性通過依賴注入的方式來初始化。這種情況下,你就可以使用lateinit關(guān)鍵字來修飾聲明的屬性。
如:

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // 直接訪問屬性
    }
}

使用限制:

  1. 只能用于var屬性
  2. 只能用于類體內(nèi)聲明的屬性
  3. 屬性不能有自定義訪問器
  4. 屬性必須是非null
  5. 屬性不能是基本類型
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容