聲明屬性
Kotlin 類中的屬性既可以用關(guān)鍵字
var
聲明為可變的,也可以用關(guān)鍵字val
聲明為只讀的。
class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
- 使用屬性,只要用名稱引用它即可浙宜。
fun copyAddress(address: Address): Address {
val result = Address() // Kotlin 中沒有“new”關(guān)鍵字
result.name = address.name // 將調(diào)用訪問(wèn)器
result.street = address.street
// ……
return result
}
Getters 與 Setters
- 聲明一個(gè)屬性的完整語(yǔ)法如下:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
- 其初始器
(initializer)
、getter
和setter
都是可選的蛹磺。屬性類型如果可以從初始器 (或者從其getter
返回值粟瞬,如下文所示)中推斷出來(lái),也可以省略萤捆。
var allByDefault: Int? // 錯(cuò)誤:需要顯式初始化器裙品,隱含默認(rèn) getter 和 setter
var initialized = 1 // 類型 Int、默認(rèn) getter 和 setter
- 一個(gè)只讀屬性的語(yǔ)法和一個(gè)可變的屬性的語(yǔ)法有兩方面的不同:
1俗或、只讀屬性的用val
開始代替var
2市怎、只讀屬性不允許setter
val simple: Int? // 類型 Int、默認(rèn) getter辛慰、必須在構(gòu)造函數(shù)中初始化
val inferredType = 1 // 類型 Int 区匠、默認(rèn) getter
- 我們可以為屬性定義自定義的訪問(wèn)器,如果我們定義了一個(gè)自定義的
getter
帅腌,那么每次訪問(wèn)該屬性時(shí)都會(huì)調(diào)用它驰弄;如果可以從getter
中推斷出返回類型,則可以省略它速客。
val isEmpty: Boolean
get() = this.size == 0
val isEmpty get() = this.size == 0 // 具有類型 Boolean
- 如果我們定義了一個(gè)自定義的
setter
戚篙,那么每次給屬性賦值時(shí)都會(huì)調(diào)用它(按照慣例,setter
參數(shù)的名稱是value
溺职,但是如果你喜歡你可以選擇一個(gè)不同的名稱)岔擂。
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // 解析字符串并賦值給其他屬性
}
- 如果你需要改變一個(gè)訪問(wèn)器的可見性或者對(duì)其注解,但是不需要改變默認(rèn)的實(shí)現(xiàn)辅愿, 你可以定義訪問(wèn)器而不定義其實(shí)現(xiàn):
var setterVisibility: String = "abc"
private set // 此 setter 是私有的并且有默認(rèn)實(shí)現(xiàn)
var setterWithAnnotation: Any? = null
@Inject set // 用 Inject 注解此 setter
幕后字段
在 Kotlin 類中不能直接聲明字段智亮。當(dāng)一個(gè)屬性需要一個(gè)幕后字段時(shí),可以使用
field
標(biāo)識(shí)符在訪問(wèn)器中引用点待。需要注意的是阔蛉,field
標(biāo)識(shí)符只能用在屬性的訪問(wèn)器內(nèi)。
var counter = 0 // 注意:這個(gè)初始器直接為幕后字段賦值
set(value) {
if (value >= 0) field = value
}
幕后屬性
如果你的需求不符合這套“隱式的幕后字段”方案癞埠,那么可以使用 幕后屬性(backing property)
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // 類型參數(shù)已推斷出
}
return _table ?: throw AssertionError("Set to null by another thread")
}
編譯期常量
如果只讀屬性的值在編譯期是已知的状原,那么可以使用
const
修飾符將其標(biāo)記為編譯期常量。
這種屬性需要滿足以下:
- 位于頂層或者是
object
聲明 或companion object
的一個(gè)成員- 以
String
或原生類型值初始化- 沒有自定義 getter
const val CONST_VAL = 1
const val CONST_VAL_GET get() = 1 // error: 不能有自定義 getter
const val CONST_VAL_TEST :Any = 1 // error 需要是 String 或原生類型值初始化
fun testConstInFunction() {
const val CONST_VAL = 1 // error: 需要位于頂層或者是 object 聲明 或 companion object 的一個(gè)成員
}
object Kotlin {
const val CONST_VAL: String = "object 常量"
}
- 這些屬性還可以用在注解中:
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… }
延遲初始化屬性與變量
一般地苗踪,屬性聲明為非空類型必須在構(gòu)造函數(shù)中初始化颠区。 然而,這經(jīng)常不方便通铲。例如:屬性可以通過(guò)依賴注入來(lái)初始化毕莱, 或者在單元測(cè)試的 setup 方法中初始化。 這種情況下,你不能在構(gòu)造函數(shù)內(nèi)提供一個(gè)非空初始器朋截。 但你仍然想在類體中引用該屬性時(shí)避免空檢測(cè)蛹稍。
- 針對(duì)上面情況,Kotlin 引入了
lateinit
修飾符部服,用于標(biāo)記該屬性唆姐。
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // 直接解引用
}
}
lateinit
修飾符只能用于在類體中的屬性(不是在主構(gòu)造函數(shù)中聲明的 var
屬性,并且僅當(dāng)該屬性沒有自定義 getter
或 setter
時(shí))廓八。
自 Kotlin 1.2 起奉芦,也用于頂層屬性與局部變量。該屬性或變量必須為非空類型剧蹂,并且不能是原生類型声功。
在初始化前訪問(wèn)一個(gè) lateinit
屬性會(huì)拋出一個(gè)特定異常,該異常明確標(biāo)識(shí)該屬性被訪問(wèn)及它沒有初始化的事實(shí)国夜。
- 檢測(cè)一個(gè) lateinit var 是否已初始化(自 1.2 起)
Kotlin 可以通過(guò) .isInitialized 檢測(cè)一個(gè)
lateinit var
是否已經(jīng)初始化過(guò):
if (foo::bar.isInitialized) {
println(foo.bar)
}
注意:此檢測(cè)僅對(duì)可詞法級(jí)訪問(wèn)的屬性可用减噪,即聲明位于同一個(gè)類型內(nèi)、位于其中一個(gè)外圍類型中或者位于相同文件的頂層的屬性车吹。
覆蓋屬性
委托屬性
- 最常見的一類屬性就是簡(jiǎn)單地從幕后字段中讀取(以及可能的寫入)醋闭。
- 另一方面窄驹,使用自定義 getter 和 setter 可以實(shí)現(xiàn)屬性的任何行為。
- 介于兩者之間证逻,屬性如何工作有一些常見的模式乐埠。一些例子:惰性值、 通過(guò)鍵值從映射讀取囚企、訪問(wèn)數(shù)據(jù)庫(kù)丈咐、訪問(wèn)時(shí)通知偵聽器等等。