# 屬性與字段
## 聲明屬性
Kotlin 中的類可以有屬性。`var` 關鍵字聲明可變量或是使用 `val` 關鍵字聲明為只讀。
``` kotlin
public class Address {
public var name: String = ...
public var street: String = ...
public var city: String = ...
public var state: String? = ...
public var zip: String = ...
}
```
要使用一個屬性,只需簡單地通過名稱提交給它,如同 Java 中的字段一樣:
``` kotlin
fun copyAddress(address: Address): Address {
val result = Address() // Kotlin 不需要 new
result.name = address.name // 已經調用了訪問器
result.street = address.street
// ...
return result
}
```
## Getter 和 Setter
完整的屬性聲明語法是
``` kotlin
var <propertyName>: <PropertyType> [= <property_initializer>]
[<getter>]
[<setter>]
```
初始器、getter 和 setter 都是可選的。如果屬性類型可以從初始器推斷出或是從基類成員覆蓋的話,那么也是可選的。
示列:
``` kotlin
var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter
```
兩種區別只讀屬性聲明與可變屬性的完整的語法為:以 `val` 開始代替 `var` 并且不允許有 setter:
``` kotlin
val simple: Int? // 擁有 Int 屬性,默認的 getter,必須要構造器中初始化
val inferredType = 1 // 擁有 Int 屬性和一個默認的 getter
```
我們可以自定義訪問器,非常類似于普通函數,就在屬性聲明下面。這里是一個自定義 getter 的示例:
``` kotlin
val isEmpty: Boolean
get() = this.size == 0
```
一個自定義 setter 是這樣的:
``` kotlin
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
```
按照約定,setter 的參數是 `value`,但如果你喜歡你可以選擇一個不同的名稱。
如果你需要改變一個訪問器的可見性或者要注釋它,但不需要改變默認的實現,你可以定義訪問器但不定義它的函數體:
``` kotlin
var setterVisibility: String = "abc" // 必須初始化,不能是空類型
private set // setter 是私有的并擁有默認的實現
var setterWithAnnotation: Any?
@Inject set // 用 Inject 注釋 setter
```
### 后臺字段
Kotlin 中的類不能有字段。然而有時在使用自定義訪問器時它有必要有一個后臺字段。對于這些需求,Kotlin 自動提供一個后臺字段,可以使用 `field` 標識訪問:
``` kotlin
var counter = 0 // the initializer value is written directly to the backing field
set(value) {
if (value >= 0)
field = value
}
```
`field` 標識只能在屬性的訪問器中使用。
編譯器在看到訪問器的函數體,如果它們使用了后臺字段(或者訪問器并非默認實現),就會創建一個后臺字段,否則不會。
舉例,下面的案例則不會有后臺字段:
``` kotlin
val isEmpty: Boolean
get() = this.size == 0
```
### 后臺屬性
如果你要做的事不適合“隱式后臺字段”的方案,你仍然可以退一步,用**后臺屬性**:
``` kotlin
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` 修飾符標記為_編譯期常量_。這樣的屬性需要滿足下列要求:
* 一個對象的頂層
* 初始化為 `String` 類型或是原始類型
* 沒有自定義 getter
這樣的屬性可以在注解中使用:
``` kotlin
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }
```
## 延遲初始化屬性
一般地,聲明為不可為空類型的屬性必須在構造器中初始化。然而公平地說這樣常常不太方便。比如說,改改可以通過依賴注入初始化,或者在單元測試中的 setup 方法。在這種情況下,你不能在構造器中提供一個非空的初始化,但你還是要避免在類中引用這個屬性時的空檢測。
這種情況下,你可以用 `lateinit` 修飾符標記該屬性:
``` kotlin
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // dereference directly
}
}
```
這個修飾符只能用在類內部聲明的 `var` 屬性上(不是主構造體),并且只能在改改不能有一個自定義的 getter 或 setter 時。屬性的類型必須為非空,而且它絕不能是原始類型。
在一個 `lateinit` 屬性被初始化前訪問它會拋出一個特殊的異常來清楚地指明它初訪問過而事實上它還沒有被初始化。
## 覆蓋屬性
查看 [覆蓋成員](classes.html#overriding-members)
## 屬性委托
許多通常的屬性種類會簡單地從后臺字段讀取(也可能是寫入)。另外,有自定義 getter 和 setter 的可以實現一些屬性的行為。介于兩者之間,這是一個屬性確定的通用工作方式。不多的例子:延遲值,通過一個給定的 key 從映身中讀取,訪問一個數據庫,在訪問時通知監聽器等等。
這樣的通用行為可以使用_屬性委托_實現。更多的信息請看[這里](delegated-properties.html)