Kotlin學習之類與對象篇—屬性與字段

屬性的聲明

Kotlin類中可以有屬性,屬性可以被聲明爲可變屬性,用var關鍵字修飾;或者聲明爲只讀屬性,用val修飾。

class Address {
    var name: String = ...
    var street: String = ...
    var city: String = ...
    var state: String? = ...
    var zip: String = ...
}

要使用一個屬性,可以直接通過它的名字調用它:

fun copyAddress(address: Address): Address {
    val result = Address() 
    result.name = address.name 
    result.street = address.street
    // ...
    return result
}


Getters 和 Setters

聲明一個屬性的完整語法如下:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

其中initializer, gettersetter都是可選項。如果屬性的類型可推斷(或者可以從getter的返回值類型推斷),propertyType也是可選項。

例子:

var allByDefault: Int? // 報錯: 屬性未被初始化
var initialized = 1 // 屬性 type 爲 Int, 生成默認 getter 和 setter

只讀屬性的聲明和可變屬性有些不一樣,它沒有setter,畢竟只讀。

我們能自定義存取器,下面是一個自定義的getter:

val isEmpty: Boolean
    get() = this.size == 0

一個自定義的setter如下:

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) 
    }

方便起見,setter參數的名字是value,但這並不是關鍵字,你也可以給參數起其它名字。

如果需要改變存取器的可見性或者註釋它,可以只定義存取器而不定義其實現,如下:

var setterVisibility: String = "abc"
    private set // private 的 setter,擁有默認實現

var setterWithAnnotation: Any? = null
    @Inject set // 被Inject註釋的setter
(1). Backing Fields

這個不好翻譯,看到有人譯作幕後字段,也有人譯作影子字段,它的作用是在存取器中代指屬性本身。

Kotlin中不支持直接聲明字段。然而,當一個屬性需要backing field的時候,Kotlin會自動爲它提供。backing field在存取器中用標識符field引用。

var counter = 0 
    set(value) {
        if (value >= 0) field = value
    }

注意:field標識符只能在屬性的存取器中被使用。

當一個屬性默認實現至少一個存取器方法(getter/setter)或者在自定義存取器中使用field關鍵字引用的時候,它就會生成一個backing field。下面這個例子就沒有生成backing field:

val isEmpty: Boolean
    get() = this.size == 0
(2). Backing Properties

如果backing field無法滿足需求,那麼可以使用backing properties:

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() 
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

從各方面看,這與Java的方式一樣。默認通過getter和setter訪問私有屬性的優化,能免去調用函數帶來的開銷。


編譯時常量

在編譯時,值已知的屬性稱爲編譯時常量,使用修飾符const來標記。編譯時常量需要滿足以下要求:
- 位於頂層 或者 是object的成員
- 使用String或者原始數據類型初始化
- 沒有自定義的getter方法

這樣的屬性可以被用在註解中:

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

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


延遲初始化屬性和變量

正常情況下,非空類型的屬性必須在構造器中被初始化。然而,這樣通常會不方便。比如:通過依賴注入,或者在單元測試的setup方法中被初始化的屬性。這種情況下,當在類體中引用屬性時,就無法滿足在構造器中非空初始化,並且同時又要避免空值檢查。
爲了處理這種情況,可以使用lateinit修飾符來標記屬性:

public class MyTest {
    lateinit var subject: TestSubject

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

    @Test fun test() {
        subject.method()
    }
}

使用限定:lateinit修飾符只能用於在類體中用var聲明的屬性,並且屬性不能有自定義的getter或setter方法,自從Kotlin 1.2,也可用於頂層的屬性和局部變量。屬性和變量必須爲非空,而且不能是原始數據類型。

訪問一個還沒被初始化的lateinit屬性將會拋出錯誤。

檢查一個 lateinit var 是否已被初始化(自從kotlin 1.2)

要檢查一個lateinit var 是否已被初始化,可以在該屬性的引用上使用.isInitialized方法。

if (foo::bar.isInitialized) {
    println(foo.bar)
}

此檢測僅對可詞法級訪問的屬性可用,即聲明位於同一個類型內、位於其中一個外圍類型中或者位於相同文件的頂層的屬性。

上面這段話不是特別明白 … …

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章